Data Availability

All you need to do to run this workflow is to install the appropriate R packages and download the following file:

We also made several additional data products and processing scripts available. * https://doi.org/10.6084/m9.figshare.7357178: DOI for this workflow. * https://doi.org/10.6084/m9.figshare.6875522: Raw data for each sample (before removing primers). * Trimmed data (primers removed) are deposited at the European Nucleotide Archive under the study accession number PRJEB28397 (ERP110594). * https://doi.org/10.6084/m9.figshare.6997253: DOI for the DADA2 processing workflow.


Preamble

This document is interactive. You can sort and scroll through most of the tables and the phylogenetic tree is zoomable. In the upper right hand corner of the front page is a Code button. Use this to show or hide all the code in the document (default is hide) as well as download the .Rmd file which you can use to extract the code. Let’s proceed.

Important Definitions & Abbreviations

  • Amplicon Sequence Variant (ASV): Exact sequence variant—analogous to an OTU—but with single nucleotide resolution.
  • Differentially abundant (DA) feature: Taxa, ASV, etc. that is disproportionately abundant in a group of samples and statistically different than other groups.

Our goals of this study were to:

  1. Assess the taxonomic composition of intestinal communities from herbivorous reef fish.
  2. Determine the diversity of these communities and their similarity/dissimilarity.
  3. Identify differentially abundant ASVs across the host species.
  4. Predict the specificity of differentially abundant ASVs.

Workflow overview

Part I: Field Observations

Before we proceed with microbial community analysis, we will run some analyses on field-based behavioral assays of the different herbivorous reef fish species.

Part II: Data Preparation

In this first part we go through the steps of defining sample groups, creating phyloseq objects, removing unwanted samples, and removing contaminant ASVs. Various parts of this section can easily be modified to perform different analyses. For example, if you were only interested in a specific taxa or group of samples, you could change the code here to create new phyloseq objects.

Part III: Community Composition & Diversity

In the second part, we assess taxonomic composition as well as alpha and beta diversity. Phyloseq offers many options for assessing diversity, including several alpha diversity metrics, additional ordination and distance methods, and so on. You can play around with these settings to how it affects the results.

Part IV: Differentially Abundant ASVs

We wanted to understand how ASVs partitioned across host species. We also wanted to assess the specificity of each ASV to determine habitat preference. To our knowledge there is no quantitative way to do this. The only attempt we are aware of was MetaMetaDB but it is based on a 454 database and no longer seems to be in active development. So we used an approach based on the work of Sullam et. al., first identifying differentially abundant ASVs, then searching for closest database hits, and finally using phylogenetic analysis and top hit metadata (isolation source, natural host) to infer habitat preference.

Part V: Synthesis

In this section we pull together the results from Part III and try to make sense of the microbiomes from these herbivorous reef fish. How are ASVs partitioning across host? How similar are these ASVs to sequences from other studies? What can these patterns tell us about host specificity?

All tables and figures presented below are named as they appeared in the original publication. We also include many additional data productes that were not part of the original publication.


Color & graphics

Throughout this workflow we are going to rely on color to help us tell our story. We will use color to delineate host fish species, but more importantly, to delineate microbial taxa. Microbial diversity is pretty vast and it can be difficult to display all of this diversity in a single, static figure. Additionally, many of us have a decreased ability to see color or differences in color. So it is not only important to use relatively few colors but also a color blind friendly palette. For our figures, we generated a palettes based on Bang Wong’s scheme described in this paper. Wong’s color scheme uses contrasting colors that can be distinguished by folks with color vision deficiency—roughly 8% of people (mostly males) are color blind. Do want Keanu Reeves to understand your figures or not?

This scheme is conservative—there are only 7 colors. We added black and grey to give us a little wiggle room. Others have developed 12 and 15 color palette schemes, but be careful—figures with too many colors can inhibit our ability to discern patterns. Our conservative palette forces us to choose carefully when deciding which taxa to target or how many groups to display. Here we will create two palettes—one for microbial taxa with all the colors and another for the five host fish species. The latter is just a subset of the full palette. Here is the code:

#Full palette
friend_pal <- c("#009E73", "#D55E00", "#F0E442", "#CC79A7", "#56B4E9", 
    "#E69F00", "#0072B2", "#7F7F7F", "#000000", "#FFFFFF")
#Fish palette
samp_pal <- c("#CC79A7", "#0072B2", "#009E73", "#56B4E9", "#E69F00")

Part I: Field Observations

To characterize the foraging ecology of the different species of herbivorous fishes, we characterized in detail individual bites by each species at three sites in the Florida Keys (Conch, French, Molasses reefs) during the Boreal summers of 2014 and 2016. At each site, we haphazardly selected focal fish over a wide range of sizes and then randomly selected a single bite by each individual to describe (see Supplementary Table 2 for sample sizes). For each bite, we identified the food item(s) targeted as well as characteristics of the substrate (e.g., hard bottom vs. other common substrates such as sponges, gorgonians, etc.) at the precise location of the bite. For hard substrates, we recorded whether a bite was on a convex, concave, or flat surface, and whether that surface was oriented horizontally (< 45 degrees) or vertically (> 45 degrees). In addition, we framed each bite within a 5 x 5 cm micro-quadrat and measured the depth of the sediment and height of the algae at several points to determine the average sediment depth and algal height within the vicinity of the bite36. We then manually removed sediments and determined whether the fish left a distinct grazing scar (i.e., where calcium carbonate had been removed from the reef framework in addition to epilithic algae).

To visualize the multivariate patterns of herbivory we used non-metric multidimensional scaling (NMDS) as implemented via the metaMDS function in the vegan package. For each species of herbivorous fish at each site we calculated the proportion of bites focused on each prey item (‘prey variables’) as well as the proportion of bites targeting substrates with different characteristics (e.g., convex vs. concave vs. flat). We also calculated the proportion of bites resulting in a grazing scar. For bites on turf assemblages, we calculated the mean turf height and sediment depth directly adjacent to each bite. Prior to analysis, quantitative variables (e.g., sediment depth and turf height) were rescaled to the range of 0 to 1. In addition, quantitative and categorical variables were rescaled such that they would have similar influence to the ‘prey variables’ by dividing each variable by the number of prey categories. Rescaled data were then analysed via NMDS using a Bray-Curtis dissimilarity matrix.

Supplementary Table 1

First, we read in and display the table describing the foraging ecology of these fish.

library(ggthemes)
all_traits <- read.csv("MANUAL_INPUT/Mean_bite_characteristics.txt", header = TRUE, sep = "\t")
ids <- all_traits[,1:2]

write.table(all_traits, "R_OUTPUT/Supplementary_Table_1.txt", sep = "\t", 
            row.names = FALSE, quote = FALSE)

datatable(all_traits, rownames = FALSE, width = "100%", colnames = c(
        "Host species", "Site", "Sediment depth (mm)", "Turf height (mm)", 
"Prop mark on substrate", "Vertical_substrate", "Prop concave", 
"Prop convex", "Articulated coralline", "CCA", "Dictyota", "epiphytes", 
"Gorgonian", "Halimeda", "Laurencia", "sponge", "Stypopodium", "turf"), 
    caption = htmltools::tags$caption(
      style = "caption-side: bottom; text-align: left;", 
            "Supplementary Table 1: ", 
    htmltools::em("INSERT DESCRIPTION.")), 
    extensions = "Buttons", 
    options = list(columnDefs = list(list(className = "dt-left", targets = 0)), 
        dom = "Blrtip", pageLength = 5, lengthMenu = c(5, 10, 15), 
        buttons = c("csv", "copy"), scrollX = TRUE, scrollCollapse = TRUE))

Note Table scrolls horizontally.

Next we standardize the variables so that they have similar weights. First we rescale quantitative traits to be in the range 0 to 1 and divide by the number of diet categories to have similar influence as the diet variables. Next we rescale all ‘non diet’ traits to have similar influence to the diet traits by dividing by the number of diet categories divided by the number of categories for each substrate characteristic. Lastly, we combine these data into a single data frame for NMDS analysis.

#First
quant_traits_std <- decostand(all_traits[,3:4], 'range')/10
#Second
Mean_prop_mark_on_substrate_std <- all_traits[,5]/(10/2)
prop_vertical_std <- all_traits[,6]/(10/2)
prop_concave_std <- all_traits[,7]/(10/3)
prop_convex_std <- all_traits[,8]/(10/3)

all_traits_std <- cbind(quant_traits_std, 
                        Mean_prop_mark_on_substrate_std, 
                        prop_vertical_std, prop_concave_std, 
                        prop_convex_std, all_traits[,9:18])

Now we conduct the NMDS analysis of the standardized diet data.

nmds <- metaMDS(all_traits_std, distance = "bray", k = 3, trymax = 40)

Then we can inspect the Sheppard plot and save the results as a dataframe.

stressplot(nmds)

#plot(nmds, type = "t")

NMDS <- data.frame(NMDS1 = nmds$points[,1], NMDS2 = nmds$points[,2], MDS3 = nmds$points[,3])

Finally, we generate the environmental vectors (correlations of variables with ordination axes).

set.seed(1)
vec.sp <- envfit(nmds$points, all_traits[,3:18], perm=1000, choices = c(1,2,3))
spp.scrs <- as.data.frame(scores(vec.sp, display = "vectors"))
spp.scrs$species <- rownames(spp.scrs)
colnames(spp.scrs) <- c("S_MDS1", "S_MDS2", "SMDS3", "species")

Now we can plot the results and generate Figure 1 from the manuscript. For all figures generated in this workflow, we coded as much formatting as we could with R and then made minor stylistic changes in Inskscape. Thus, what you see here and throughout, are the raw figures.

#Plot results using ggplot2
#Merge with data that we want to use in plotting
NMDS <- cbind(ids, NMDS)
stuff <- ggplot(NMDS) +
    geom_point(mapping = aes(x = NMDS1, y = NMDS2, shape = Site, colour = Species), size =4)+
    geom_segment(aes(x=0,y=0,xend=S_MDS1, yend=S_MDS2), data = spp.scrs,
        arrow = arrow(length = unit(0.5, "cm")),colour="black",inherit.aes=FALSE)+
    geom_text(data=spp.scrs,aes(x=S_MDS1,y=S_MDS2,label=species),size=3)+
    labs(title = "bray distance on all traits (standardized)", subtitle = "Stress = 0.04")

#####Now subset just the spp. scrs with the highest correlations
spp.scrs_sub <- spp.scrs[-c(5,11,12),]
#Categorize species scores into different variable types for plotting
variable_type <- as.factor(c(rep("substrate", 5), rep("algae",8))) 
spp.scrs_sub <- cbind(variable_type, spp.scrs_sub)
spp.scrs_substrate <- subset(spp.scrs_sub, spp.scrs_sub$variable_type == "substrate")
spp.scrs_algae <- subset(spp.scrs_sub, spp.scrs_sub$variable_type == "algae")

Figure 1

Fig1 <- ggplot(NMDS) +
    geom_point(mapping = aes(x = NMDS1, y = NMDS2, shape = Site, colour = Species), size = 4)+
    scale_colour_manual(values = samp_pal) +
    geom_segment(aes(x=0, y=0, xend=S_MDS1, yend=S_MDS2), data = spp.scrs_substrate,
        arrow = arrow(length = unit(0.5, "cm")), color = "red") +
    geom_text(data = spp.scrs_substrate, aes(x = S_MDS1, y = S_MDS2, label = species), 
              nudge_y = c(-0.025, -0.025, -0.05, 0.05, 0.05)) +
    geom_segment(aes(x=0,y=0,xend=S_MDS1, yend=S_MDS2), data = spp.scrs_algae,
        arrow = arrow(length = unit(0.5, "cm")), color = "black") +
    geom_text(data = spp.scrs_algae, aes(x = S_MDS1, y = S_MDS2, label = species), 
              nudge_y = c(-0.025, 0.025, 0.025, 0.025, 0.025, -0.025, -0.05, -0.025)) +
    theme_classic(base_size = 15)
Fig1 <- Fig1 + coord_fixed()
Fig1
Figure 1: Bray curtis distance on all traits; stress = 0.04

Figure 1: Bray curtis distance on all traits; stress = 0.04

pdf("R_OUTPUT/Figure_1.pdf")
Fig1
invisible(dev.off())

Part II: Data Preparation

back to top

Define groupings

0k, on to the microbial data. First, we load the data packet produced by the final step of the DADA2 workflow, format sample names, and define groupings. We will use the sample names to define the different groups.

Groups

  • 53 individuals
  • 3 genera
  • 7 species
load("DADA2_DATA/combo_pipeline.rdata")
samples.out <- rownames(seqtab)
subject <- sapply(strsplit(samples.out, "[[:digit:]]"), `[`, 1)
# this splits the string at first instance of a digit
sample_name <- substr(samples.out, 1, 999)  # use the whole string for individuals
genus <- substr(samples.out, 1, 2)  # use the first two letters for genus
species <- substr(samples.out, 1, 5)  # use the next three letters for species
sample_name
##  [1] "AcCoe01" "AcCoe02" "AcCoe03" "AcCoe04" "AcCoe05" "AcCoe06" "AcCoe07"
##  [8] "AcCoe08" "AcTra01" "AcTra02" "AcTra03" "AcTra04" "AcTra05" "AcTra06"
## [15] "AcTra07" "AcTra08" "AcTra09" "ScTae01" "ScTae02" "ScTae03" "ScTae04"
## [22] "ScTae05" "ScTae06" "ScTae07" "ScTae08" "ScTae09" "ScVet01" "ScVet02"
## [29] "SpAur01" "SpAur02" "SpAur03" "SpAur04" "SpAur10" "SpAur11" "SpAur12"
## [36] "SpAur13" "SpChr01" "SpVir01" "SpVir02" "SpVir03" "SpVir04" "SpVir05"
## [43] "SpVir06" "SpVir07" "SpVir08" "SpVir09" "SpVir10" "SpVir11" "SpAur05"
## [50] "SpAur06" "SpAur07" "SpAur08" "SpAur09"
unique(genus)
## [1] "Ac" "Sc" "Sp"
unique(species)
## [1] "AcCoe" "AcTra" "ScTae" "ScVet" "SpAur" "SpChr" "SpVir"

And finally we define a sample data frame that holds the different groups we extracted from the sample names. On the right are a few samples and their different groups names.

#define a sample data frame
samdf <- data.frame(SamName = sample_name, Gen = genus, Sp = species)  
rownames(samdf) <- samples.out
kable(samdf[c(1, 13, 20, 30, 44),1:3], row.names = FALSE) %>%
  kable_styling(bootstrap_options = "striped", full_width = FALSE, position = "float_right")  %>%
  column_spec(1:3, width = "3.5cm")
SamName Gen Sp
AcCoe01 Ac AcCoe
AcTra05 Ac AcTra
ScTae03 Sc ScTae
SpAur02 Sp SpAur
SpVir07 Sp SpVir

Abbreviations:

  • AcCoe = Acanthurus coeuleus
  • AcTra = Acanthurus tractus
  • ScTae = Scarus taeniopterus
  • SpAur = Sparisoma aurofrenatum
  • SpVir = Sparisoma viride
  • ScVet = Scarus vetula
  • SpChr = Sparisoma chrysopterum

Create & modify a phyloseq object

Next we create a phyloseq (ps) object with the Silva (slv) taxonomy. There is also a Greengenes (gg) annotation in the output file from DADA2 which can be used instead of the Silva annotation. Just change tax_silva to tax_gg. At this point we rename the amplicon sequence variants (ASVs) so the designations are a bit more user friendly. By default, DADA2 names each ASV by its unique sequence so that data can be directly compared across studies (which is great). But this convention can get cumbersome downstream, so we rename the ASVs using a simpler convention—ASV1, ASV2, ASV3, and so on, while retaining the exact sequences.

ps_slv <- phyloseq(otu_table(seqtab, taxa_are_rows = FALSE), sample_data(samdf), 
    tax_table(tax_silva)) # this create the phyloseq object
tax_table(ps_slv) <- cbind(tax_table(ps_slv), rownames(tax_table(ps_slv)))
taxa_names(ps_slv) <- paste0("ASV", seq(ntaxa(ps_slv))) # adding unique ASV names
tax_table(ps_slv) <- cbind(tax_table(ps_slv), rownames(tax_table(ps_slv)))
head(taxa_names(ps_slv))
## [1] "ASV1" "ASV2" "ASV3" "ASV4" "ASV5" "ASV6"
ps_slv
## phyloseq-class experiment-level object
## otu_table()   OTU Table:         [ 12479 taxa and 53 samples ]
## sample_data() Sample Data:       [ 53 samples by 3 sample variables ]
## tax_table()   Taxonomy Table:    [ 12479 taxa by 8 taxonomic ranks ]

And then we add two final columns with the actual ASV sequences and ASV IDs. This will be useful later when trying to export a fasta file.

colnames(tax_table(ps_slv)) <- c("Kingdom", "Phylum", "Class", "Order", 
    "Family", "Genus", "ASV_SEQ", "ASV_ID")

At this point we have a completely unadulterated phyloseq object because it contains all the ASVs and all samples. Lets export the sequence and taxonomy tables for posterity sake.

write.table(tax_table(ps_slv), "R_OUTPUT/full_tax_table.txt", sep="\t", quote = FALSE, col.names=NA)
write.table(t(otu_table(ps_slv)), "R_OUTPUT/full_seq_table.txt", sep="\t", quote = FALSE, col.names=NA)
write.table(sample_data(ps_slv), "R_OUTPUT/full_sample_data.txt", sep="\t", quote = FALSE, row.names =  FALSE)

Remember three of these samples were omitted because we did not have replicates for the host species. Lets remove those samples. The only way we could figure out how to do this was by selecting the samples we wanted to keep. If you want to change the group of samples, modify the script accordingly.

ps_slv_base <- prune_samples(c("SpAur01", "SpAur02", "SpAur03", "SpAur04", 
    "SpAur10", "SpAur11", "SpAur12", "SpAur13", "SpVir01", "SpVir02", "SpVir03", 
    "SpVir04", "SpVir05", "SpVir06", "SpVir07", "SpVir08", "SpVir09", "SpVir10", 
    "SpVir11", "AcCoe01", "AcCoe02", "AcCoe03", "AcCoe04", "AcCoe05", "AcCoe06", 
    "AcCoe07", "AcCoe08", "AcTra01", "AcTra02", "AcTra03", "AcTra04", "AcTra05", 
    "AcTra06", "AcTra07", "AcTra08", "AcTra09", "ScTae01", "ScTae02", "ScTae03", 
    "ScTae04", "ScTae05", "ScTae06", "ScTae07", "ScTae08", "ScTae09", "SpAur05", 
    "SpAur06", "SpAur07", "SpAur08", "SpAur09"), ps_slv)
ps_slv_base
## phyloseq-class experiment-level object
## otu_table()   OTU Table:         [ 12479 taxa and 50 samples ]
## sample_data() Sample Data:       [ 50 samples by 3 sample variables ]
## tax_table()   Taxonomy Table:    [ 12479 taxa by 8 taxonomic ranks ]

0K, three samples gone. But we probably lost some ASVs when use we removed samples. So we need to get rid of any ASVs that have now a total of 0 reads. This will be our working phyloseq object.

ps_slv_work <- prune_taxa(taxa_sums(ps_slv_base) > 0, ps_slv_base)
ps_slv_work
## phyloseq-class experiment-level object
## otu_table()   OTU Table:         [ 12040 taxa and 50 samples ]
## sample_data() Sample Data:       [ 50 samples by 3 sample variables ]
## tax_table()   Taxonomy Table:    [ 12040 taxa by 8 taxonomic ranks ]

Looks like 439 ASVs were only found in those three samples.

Remove contaminants

These samples are intestinal communities and we assume that Chloroplast are not contributing to metabolism. These data could be useful later but for now lets create a phyloseq object without Chloroplast.

WARNING: the subset_taxa command removes anything that is NA for the specified taxonomic level or above. For example, lets say you run the subset_taxa command using Order != "Chloroplast". Seems like you should get a phyloseq object with everything except Chloroplast. But actually the command not only gets rid Chloroplast but everything else that has NA for Order and above. In our experience this is not well documented and we had to dig through the files to figure out what was happening.

Our dataset has 590 Chloroplast ASVs and running the command as is removed an additional 1244 ASVs. So lets see if we can get rid of just Chloroplast ASVs without removing everything that is unclassified at Order and above. To do this, we subset the taxa to generate a ps object of just Chloroplast, selected the ASV column only, turned it into a factor, and used this to remove Chloroplast from the ps object.

# generate a file with Chloroplast ASVs
CH1 <- subset_taxa(ps_slv_work, Order == "Chloroplast")
CH1 <-  as(tax_table(CH1), "matrix")
CH1 <- CH1[, 8]
CH1df <- as.factor(CH1)
goodTaxaCH <- setdiff(taxa_names(ps_slv_work), CH1df)
ps_slv_work_no_cyano <- prune_taxa(goodTaxaCH, ps_slv_work)
ps_slv_work_no_cyano
## phyloseq-class experiment-level object
## otu_table()   OTU Table:         [ 11450 taxa and 50 samples ]
## sample_data() Sample Data:       [ 50 samples by 3 sample variables ]
## tax_table()   Taxonomy Table:    [ 11450 taxa by 8 taxonomic ranks ]

This step removed 590 Chloroplast ASVs. Perfect.

And now we use the same approach to remove Mitochondria.

# generate a file with mitochondria ASVs
MT1 <- subset_taxa(ps_slv_work_no_cyano, Family == "Mitochondria")
MT1 <-  as(tax_table(MT1), "matrix")
MT1 <- MT1[, 8]
MT1df <- as.factor(MT1)
goodTaxa <- setdiff(taxa_names(ps_slv_work_no_cyano), MT1df)
ps_slv_work_filt <- prune_taxa(goodTaxa, ps_slv_work_no_cyano)
ps_slv_work_filt
## phyloseq-class experiment-level object
## otu_table()   OTU Table:         [ 11144 taxa and 50 samples ]
## sample_data() Sample Data:       [ 50 samples by 3 sample variables ]
## tax_table()   Taxonomy Table:    [ 11144 taxa by 8 taxonomic ranks ]

Sweet, looks like this removed 306 Mitochondria ASVs. Next we generate some summary data for each sample.

Phyloseq object merged by species

One last thing to do is to create a merged phyloseq object grouped by host species. This will come in handy later for some analyses. Basically we collapsed all samples from the same host species together.

mergedGP <- merge_samples(ps_slv_work_filt, "Sp")
SD <- merge_samples(sample_data(ps_slv_work_filt), "Sp")
mergedGP
## phyloseq-class experiment-level object
## otu_table()   OTU Table:         [ 11144 taxa and 5 samples ]
## sample_data() Sample Data:       [ 5 samples by 3 sample variables ]
## tax_table()   Taxonomy Table:    [ 11144 taxa by 8 taxonomic ranks ]
sample_names(mergedGP)
## [1] "AcCoe" "AcTra" "ScTae" "SpAur" "SpVir"

Great, still the same number of ASVs and now only 5 “samples” corresponding to the 5 species.

There are now the several phyloseq objects to chose from and, using the above methods, additional objects can easily be created.

  • ps_slv –> phyloseq dataset with all 53 samples, all ASVs.
  • ps_slv_base –> phyloseq dataset with 50 samples, all ASVs (this is not very useful).
  • ps_slv_work –> phyloseq dataset with 50 samples, 0 read ASVs removed.
  • ps_slv_work_filt –> phyloseq dataset with 50 samples, ASVs from Mitochondria and Cyanobacteria removed.
  • mergedGP –> ps_slv_work_filt phyloseq dataset collapsed by host species.

Host Information

Before we do anything else, lets generate summary data for each host. We can generate a summary report for any ps object but we will use the object with mitochondria and chlorplasts removed, as well as the low replicate host species removed. We will also add details about each host. The table is displayed below. We can use these data when we upload the original fastq files to sequence read archives. Later on we will also add alpha diversity stats and save the table.


Host details

total_reads <- sample_sums(ps_slv_work_filt)
total_reads <- as.data.frame(total_reads, make.names = TRUE)
total_reads <- total_reads %>% rownames_to_column("host_ID")

total_asvs <- estimate_richness(ps_slv_work_filt, measures = "Observed")
total_asvs <- total_asvs %>% rownames_to_column("host_ID")

sam_details <- sample_data(ps_slv)
sam_details <- sam_details %>% mutate(genus = case_when(
    Gen == "Ac" ~ "Acanthurus", 
    Gen == "Sc" ~ "Scarus",
    Gen == "Sp" ~ "Sparisoma"))

sam_details <- sam_details %>% mutate(species = case_when(
    Sp == "AcCoe"~ "coeruleus",
    Sp == "AcTra"~ "tractus",
    Sp == "ScTae"~ "taeniopterus",
    Sp == "SpAur"~ "aurofrenatum",
    Sp == "SpVir"~ "viride"))
#Sp == "SpChr"~ "chrysopterum",
#Sp == "ScVet"~ "vetula"
sam_details <- sam_details %>% mutate(common_name = case_when(
    Sp == "AcCoe" ~ "blue tang surgeonfish", 
    Sp == "AcTra" ~ "fiveband surgeonfish", 
    Sp == "ScTae" ~ "princess parrotfish", 
    Sp == "SpAur" ~ "redband parrotfish", 
    Sp == "SpVir" ~ "stoplight parrotfish"))

#Sp == "SpChr" ~ "redtail parrotfish",
#Sp == "ScVet" ~ "queen parrotfish"))

sam_details <- sam_details %>% mutate(NCBI_txid = case_when(
    Sp == "AcCoe" ~ "157585", 
    Sp == "AcTra" ~ "1316013", 
    Sp == "ScTae" ~ "544418", 
    Sp == "SpAur" ~ "59663", 
    Sp == "SpVir" ~ "59666"))
#Sp == "SpChr" ~ "51766",
#Sp == "ScVet" ~ "84543"))

sam_details <- sam_details[-c(2,3)]
colnames(sam_details) <- c("host_ID", "host_genus", "host_species", 
    "full_name", "NCBI_txid")

merge_tab <- merge(sam_details, total_reads, by = "host_ID")
merge_tab2 <- merge(merge_tab, total_asvs, by = "host_ID")
colnames(merge_tab2) <- c("host_ID", "host_genus", "host_species", 
    "common_name", "NCBI_txid",  "total_reads", "total_ASVs")

# We also have a datatable containing metrics for each host. Lets bring this in 
# and merge with  the summary table
metrics <- read.table("MANUAL_INPUT/host_metrics.txt", sep = "\t", header = TRUE)
host_details <- merge(merge_tab2, metrics, by = "host_ID")
colnames(host_details) <- c("host_ID", "host_genus", "host_species", 
           "common_name", "NCBI_txid",  "total_reads", "total_ASVs", 
           "collection_date", "phase", "weight", "total_length", 
           "foregut_length", "midgut_length", 
           "hindgut_length", "total_gut_length")

datatable(host_details, rownames = FALSE, width = "100%", colnames = c(
        "host_ID", "host_genus", "host_species", "common_name", "NCBI_txid",  
        "total_reads", "total_ASVs", "Collection_date", "Phase", "Weight (g)", 
        "Total length (cm)", "Fore gut length (cm)", "Mid gut length (cm)", 
        "Hind gut length (cm)", "Total gut length (cm)"), 
    caption = htmltools::tags$caption(style = "caption-side: bottom; text-align: left;", 
    "Table: ", htmltools::em("Sample summary.")), extensions = "Buttons", 
    options = list(columnDefs = list(list(className = "dt-left", targets = 0)), 
        dom = "Blfrtip", pageLength = 5, lengthMenu = c(5, 10, 25, 50), buttons = c("csv", 
            "copy"), scrollX = TRUE, scrollCollapse = TRUE))

Now we have a nice little summary table about each sample—genus/species, common name, number of reads, number of ASVs, etc. All of this info can be used when submitting samples to sequence read archives. Once we conduct alpha diversity estimates below, we will add that data to the table above and export as Supplementary Table 3.


Part III: Community Composition & Diversity

back to top

What are the dominant taxa in this system? How diverse are these communities? How similar are samples to each other?

Before we can start to understand a system, we need to know something about its parts. So lets start with a quick look at class-level diversity. Of course, you can change this to any taxonomic rank you wish. Here we created a sortable table that has the total number of reads and ASVs for each class


Community Composition

Taxonomic composition: Total reads & ASVs by Class

Supplementary Table 4

# generate the ASV table
tax_asv <- table(tax_table(ps_slv_work_filt)[, "Class"], exclude = NULL, 
    dnn = "Taxa")
tax_asv <- as.data.frame(tax_asv, make.names = TRUE)
# generate the reads table
tax_reads <- factor(tax_table(ps_slv_work_filt)[, "Class"])
tax_reads <- apply(otu_table(ps_slv_work_filt), MARGIN = 1, function(x)
{
    tapply(x, INDEX = tax_reads, FUN = sum, na.rm = TRUE, simplify = TRUE)
})
tax_reads <- as.data.frame(tax_reads, make.names = TRUE)
tax_reads <- cbind(tax_reads, reads = rowSums(tax_reads))
tax_reads <- tax_reads[51]
tax_reads <- setDT(tax_reads, keep.rownames = TRUE)[]
# merge the two tables and make everything look pretty
# in an interactive table
taxa_read_asv_tab <- merge(tax_reads, tax_asv, by.x = "rn", by.y = "Taxa")
names(taxa_read_asv_tab) <- c("Taxa", "total reads", "total ASVs")

write.table(taxa_read_asv_tab, "R_OUTPUT/Supplementary_Table_4.txt", sep = "\t", 
            row.names = FALSE, quote = FALSE)

datatable(taxa_read_asv_tab, rownames = FALSE, width = "100%", colnames = c(
            "Taxa", "total reads", "total ASVs"), 
            caption = htmltools::tags$caption(
              style = "caption-side: bottom; text-align: left;", 
    "Supplementary Table 4: ", htmltools::em("Total reads & ASVs by Class")), extensions = "Buttons", 
    options = list(columnDefs = list(list(className = "dt-left", targets = 0)), 
        dom = "Blfrtip", pageLength = 5, lengthMenu = c(5, 10, 35, 70), buttons = c("csv", 
            "copy")))

Looks like Proteobacteria, Firmicutes, Fusobacteria, Planctomycetes, and Bacteroidetes dominate in the read department. Curiously, Fusobacteria has comparatively low ASV richness.


Much of the analyses we do from here on out will be at the Class & Family levels. We chose not to focus on the Genus level because there simply is not enough resolution in our dataset to build a cohesive story. This is because these fish are (microbially) understudied and we are dealing with short read data. On the other hand, Phylum level is too coarse for groups like Proteobacteria and Firmicutes. Order did not provide any additional information and can be cumbersome for taxa with poorly resolved lineages. Depending on the dataset, you may want to change your strategy.

Lets take a closer look Class-level taxonomic content of these communities. There are numerous ways to do this but here we chose to collapse samples by host species and display the relative abundance of the most dominant taxa. We also generated an alternative view of taxonomic composition for individual samples—separate bar plots that are included as Supplementary Figure 1 (see below for code).

Stacked bar charts are not the best but we like them for a birds eye view of the data. Here we calculate the relative abundance of taxa for each host species at the Class level. It turns out this is not too easy in phyloseq and there is a lot of (messy) code.

# calculate the averages and merge by species
ps_slv_filt_AVG <- transform_sample_counts(ps_slv_work_filt, function(x) x/sum(x))
mergedGP_BAR <- merge_samples(ps_slv_filt_AVG, "Sp")
SD_BAR <- merge_samples(sample_data(ps_slv_filt_AVG), "Sp")

# merge taxa by rank. If you choose a different rank be sure to change
# the rank throughout this code chunk
mdata_phy <- tax_glom(mergedGP_BAR, taxrank = "Class", NArm = FALSE)  
mdata_phyrel <- transform_sample_counts(mdata_phy, function(x) x/sum(x))
meltd <- psmelt(mdata_phyrel)
meltd$Class <- as.character(meltd$Class)

# calculate the total relative abundance for all taxa
means <- ddply(meltd, ~Class, function(x) c(mean = mean(x$Abundance)))
means$mean <- round(means$mean, digits = 8)
taxa_means <- means[order(-means$mean), ]  # this order in decending fashion
taxa_means <- format(taxa_means, scientific = FALSE)  # ditch the sci notation 

Since our goal is to generate a figure and we only have 9 colors, some taxa will need to be put into an Other category. We can define ‘Other’ however we like so lets take a look at the overall relative abundance of each Class.


Taxonomic composition: Class-level relative abundance

datatable(taxa_means, rownames = FALSE, width = "65%", colnames = c("Class", 
    "mean"), 
    caption = htmltools::tags$caption(style = 
        "caption-side: bottom; text-align: left;", "Table 2: ", 
        htmltools::em("Class-level relative abundance.")), 
    extensions = "Buttons", options = list(columnDefs = 
        list(list(className = "dt-center", targets = "_all")), 
         dom = "Blfrtip", pageLength = 5, lengthMenu = c(5, 10, 50, 70), 
         buttons = c("csv", "copy")))
write.table(taxa_means, "R_OUTPUT/class_rel_abund.txt", sep = "\t", row.names = FALSE, 
    quote = FALSE)

Inspecting the table it looks like if we choose a cutoff of 2% (0.02) we get 9 taxa—sounds pretty good. The rest go into the ‘Other’ category. No matter what, we will always gloss over some groups using such a coarse approach. But as we will see later, some of these low abundance groups will reappear when we look at the level of individual ASVs.

Here we define the Other category.


# Here we conglomerate at 2%.
Other <- means[means$mean <= 0.02, ]$Class  
# or you can chose specifc taxa like this
#Other_manual <- c("list", "taxa", "in", "this", "format")
length(Other)
## [1] 63

At a 2% abundance cutoff, 63 Classes are grouped into the ‘Other’ category. Great, now we can craft the bar chart. And here are the taxa that will go into the chart:

meltd[meltd$Class %in% Other, ]$Class <- "Other"
samp_names <- aggregate(meltd$Abundance, by = list(meltd$Sample), FUN = sum)[, 1]
.e <- environment()
meltd[, "Class"] <- factor(meltd[, "Class"], sort(unique(meltd[, "Class"])))
meltd <- meltd[order(meltd[, "Class"]), ]
# Here we order Classes by the Phylum they belong to.
levels(meltd$Class)
##  [1] "Alphaproteobacteria" "Bacteroidia"         "Clostridia"         
##  [4] "Deltaproteobacteria" "Erysipelotrichia"    "Fusobacteriia"      
##  [7] "Gammaproteobacteria" "Other"               "Oxyphotobacteria"   
## [10] "Planctomycetacia"
meltd$Class <- factor(meltd$Class, levels = c("Bacteroidia", "Clostridia", 
    "Erysipelotrichia", "Fusobacteriia", "Alphaproteobacteria", "Deltaproteobacteria", 
    "Gammaproteobacteria", "Planctomycetacia", "Oxyphotobacteria", "Other"))  

It took some tweaking to get the bar chart to look just right—so there is a lot of code here—and it could most certainly be better. While we’re at it, we will also save a copy of the figure so we can tweak it later and make it look pretty.


Taxonomic composition: Class abundance across host species

Figure 2A

fig2A <- ggplot(meltd, aes_string(x = "Sample", y = "Abundance", fill = "Class"), 
    environment = .e, ordered = TRUE, xlab = "x-axis label", ylab = "y-axis label") 
fig2A <- fig2A + geom_bar(stat = "identity", position = position_stack(reverse = TRUE), 
    width = 0.95) + coord_flip() + theme(aspect.ratio = 1/2)
fig2A <- fig2A + scale_fill_manual(values = friend_pal)
fig2A <- fig2A + theme(axis.text.x = element_text(angle = 0, hjust = 0.45, 
    vjust = 1))
fig2A <- fig2A + guides(fill = guide_legend(override.aes = list(colour = NULL), 
    reverse = FALSE)) + theme(legend.key = element_rect(colour = "black"))
fig2A <- fig2A + labs(x = "Host species", y = "Relative abundance (% total reads)", 
    title = "Abundance of bacterial taxa across host species")
fig2A <- fig2A + theme(axis.line = element_line(colour = "black"), panel.grid.major = element_blank(), 
    panel.grid.minor = element_blank(), panel.border = element_rect(colour = "black", 
        fill = NA, size = 1))
fig2A
Figure 2A

Figure 2A

pdf("R_OUTPUT/Figure_2A.pdf")
fig2A
invisible(dev.off())

Community Diversity

Armed with a picture of taxonomic composition we can move on to diversity estimates.

We took two views of overall community diversity. Just click on a tab to see the code and results.

\(\alpha\)-diversity stats

Alpha diversity describes the diversity in a sample or site. There are several alpha diversity metrics available in phyloseq: Observed, Chao1, ACE, Shannon, Simpson, InvSimpson, Fisher. Play around to see how different metrics change or confirm these results.

Here we want to know if diversity is significantly different across host species. In order to do that we need to know if we should run a parametric or non-parametric test, and for that we need to know if our data is normally distributed. Most of the ideas/code for alpha (and subsequent beta) diversity statistics come from this workshop tutorial by Kim Dill-McFarland and Madison Cox.

First we run the diversity estimates, add these data to our summary table, and save a copy of this table.

Supplementary Table 3

diversity <- estimate_richness(ps_slv_work_filt, measures=c(
            "Observed", "Chao1", "ACE", "Shannon", "Simpson", "InvSimpson", "Fisher"))
diversity_calc <- diversity %>% rownames_to_column("host_ID")
# round values
diversity_calc[c(3,5,10)] <- round(diversity_calc[c(3,5,10)], 1)
diversity_calc[c(4,6,7,9)] <- round(diversity_calc[c(4,6,7,9)], 2)
diversity_calc[8] <- round(diversity_calc[8], 3)

host_summary <- merge(host_details, diversity_calc)
host_summary$Observed <- NULL

write.table(host_summary, "R_OUTPUT/Supplementary_Table_3.txt", 
            sep = "\t", row.names = FALSE, quote = FALSE)

Next, we add the diversity estimates to our phyloseq object, and test if the data are normally distributed using Shapiro-Wilk Normality test. We will focus on the inverse Simpson and Shannon diversity estimates and Chao’s richness estimate but this approach can be used for any metric.

# Convert to ps object
sample_div <- sample_data(diversity) 
# Create new ps object with diversity estimates added to sample_data
ps_slv_work_filt_div <- merge_phyloseq(ps_slv_work_filt, sample_div)
# Run Shapiro test
shapiro_test_Shan <- shapiro.test(sample_data(ps_slv_work_filt_div)$Shannon)
shapiro_test_invSimp <- shapiro.test(sample_data(ps_slv_work_filt_div)$InvSimpson)
shapiro_test_Chao1 <- shapiro.test(sample_data(ps_slv_work_filt_div)$Chao1)
shapiro_test_Observed <- shapiro.test(sample_data(ps_slv_work_filt_div)$Observed)

Shapiro-Wilk Normality Test for Shannon index.

## 
##  Shapiro-Wilk normality test
## 
## data:  sample_data(ps_slv_work_filt_div)$Shannon
## W = 0.98228, p-value = 0.6513

Shapiro-Wilk Normality Test for inverse Simpson index.

## 
##  Shapiro-Wilk normality test
## 
## data:  sample_data(ps_slv_work_filt_div)$InvSimpson
## W = 0.60454, p-value = 2.276e-10

Shapiro-Wilk Normality Test for Chao1 richness estimator.

## 
##  Shapiro-Wilk normality test
## 
## data:  sample_data(ps_slv_work_filt_div)$Chao1
## W = 0.89305, p-value = 0.0002855

Shapiro-Wilk Normality Test for Observed ASV richness estimator.

## 
##  Shapiro-Wilk normality test
## 
## data:  sample_data(ps_slv_work_filt_div)$Observed
## W = 0.89591, p-value = 0.0003531

Ok, since the p-values are significant for the inverse Simpson, Chao richness, and Observed ASV richness we reject the null hypothesis that these data are normally distributed. However, the Shannon estimates appear normally distributed. So lets see if diversity is significantly different between host species based on the Shannon index.

Normally distributed metrics

Since the Shannon data is normally distributed we can test for significance using ANOVA (a parametric test).

sampledataDF <- data.frame(sample_data(ps_slv_work_filt_div))
aov.shannon = aov(Shannon ~ Sp, data = sampledataDF)
#Call for the summary of that ANOVA, which will include P-values
summary(aov.shannon)
##             Df Sum Sq Mean Sq F value  Pr(>F)   
## Sp           4  19.13   4.782   3.906 0.00833 **
## Residuals   45  55.10   1.224                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Ok, the results of the ANOVA are significant. Here we use the Tukey’s HSD (honestly significant difference) post-hoc test to determine which pairwise comparisons are different.

TukeyHSD(aov.shannon)
##   Tukey multiple comparisons of means
##     95% family-wise confidence level
## 
## Fit: aov(formula = Shannon ~ Sp, data = sampledataDF)
## 
## $Sp
##                   diff         lwr         upr     p adj
## AcTra-AcCoe  0.7421379 -0.78560423  2.26987998 0.6431490
## ScTae-AcCoe  0.4727303 -1.05501185  2.00047236 0.9030495
## SpAur-AcCoe -0.9230990 -2.33591246  0.48971439 0.3551484
## SpVir-AcCoe  0.3323910 -1.12853187  1.79331396 0.9664219
## ScTae-AcTra -0.2694076 -1.75153517  1.21271993 0.9852679
## SpAur-AcTra -1.6652369 -3.02859596 -0.30187785 0.0096995
## SpVir-AcTra -0.4097468 -1.82289999  1.00340634 0.9218562
## SpAur-ScTae -1.3958293 -2.75918834 -0.03247023 0.0424253
## SpVir-ScTae -0.1403392 -1.55349237  1.27281396 0.9985589
## SpVir-SpAur  1.2554901 -0.03255018  2.54353034 0.0593081

Looks like Sparisoma aurofrenatum is significantly different from Scarus taeniopterus and Acanthurus tractus.

Non-normally distributed metrics

Now we can look at the results on the inverse Simpson diversity and Chao’s richness. Since host species is categorical, we use Kruskal-Wallis (non-parametric equivalent of ANOVA) to test for significance.


Kruskal-Wallis of inverse Simpson index.

#library(FSA)
#dunnTest(InvSimpson ~ Sp, data = sampledataDF, method="bh") 
kruskal.test(InvSimpson ~ Sp, data = sampledataDF)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  InvSimpson by Sp
## Kruskal-Wallis chi-squared = 13.779, df = 4, p-value = 0.008034

Kruskal-Wallis of Chao1 richness estimator.

#dunnTest(Chao1 ~ Sp, data = sampledataDF, method="bh") 
kruskal.test(Chao1 ~ Sp, data = sampledataDF)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  Chao1 by Sp
## Kruskal-Wallis chi-squared = 13.992, df = 4, p-value = 0.007321

Kruskal-Wallis of Observed ASV richness index.

#library(FSA)
#dunnTest(Observed ~ Sp, data = sampledataDF, method="bh") 
kruskal.test(Observed ~ Sp, data = sampledataDF)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  Observed by Sp
## Kruskal-Wallis chi-squared = 14.153, df = 4, p-value = 0.006822

For the inverse Simpson, Chao1, and Observed richness the results of the Kruskal-Wallis rank sum test are significant. So we can look at pairwise comparisons using Wilcoxon rank sum test for post-hoc analysis.

Pairwise significance test for inverse Simpson index.

pairwise.wilcox.test(sampledataDF$InvSimpson, sampledataDF$Sp, p.adjust.method="fdr")
## 
##  Pairwise comparisons using Wilcoxon rank sum test 
## 
## data:  sampledataDF$InvSimpson and sampledataDF$Sp 
## 
##       AcCoe AcTra ScTae SpAur
## AcTra 0.545 -     -     -    
## ScTae 0.963 0.545 -     -    
## SpAur 0.042 0.042 0.108 -    
## SpVir 0.545 0.789 0.545 0.004
## 
## P value adjustment method: fdr

Pairwise significance test for Chao1 richness estimator.

pairwise.wilcox.test(sampledataDF$Chao1, sampledataDF$Sp, p.adjust.method="fdr")
## 
##  Pairwise comparisons using Wilcoxon rank sum test 
## 
## data:  sampledataDF$Chao1 and sampledataDF$Sp 
## 
##       AcCoe AcTra ScTae SpAur
## AcTra 0.628 -     -     -    
## ScTae 0.617 0.666 -     -    
## SpAur 0.030 0.030 0.019 -    
## SpVir 0.666 0.628 0.304 0.046
## 
## P value adjustment method: fdr

Pairwise significance test for Observed ASV richness index.

pairwise.wilcox.test(sampledataDF$Observed, sampledataDF$Sp, p.adjust.method="fdr")
## 
##  Pairwise comparisons using Wilcoxon rank sum test 
## 
## data:  sampledataDF$Observed and sampledataDF$Sp 
## 
##       AcCoe AcTra ScTae SpAur
## AcTra 0.677 -     -     -    
## ScTae 0.677 0.730 -     -    
## SpAur 0.026 0.026 0.019 -    
## SpVir 0.730 0.677 0.304 0.039
## 
## P value adjustment method: fdr

Again we see that only Sp. aurofrenatum is significantly different from the other hosts. For the inverse Simpson index, Sp. aurofrenatum is significantly different from three of the four host species and Chao1 richness estimator, Sp. aurofrenatum is significantly different from all other host species. Now we can plot the results. Click on the back to diversity tabs link to go to the alpha diversity plots.


\(\alpha\)-diversity plots

Here we plot the results of Shannon diversity index. We will save a copy of the figure for later tweaking. We use the color palette described above to delineate host species.

Figure 2B

fig2B <- plot_richness(ps_slv_work_filt, x = "Sp", 
                       measures = c("Shannon"), 
                       color = "Sp", nrow = 1)
fig2B <- fig2B + geom_boxplot() + geom_jitter(width = 0.05)
fig2B <- fig2B + scale_colour_manual(values = samp_pal) + 
         labs(x = "Host species", 
         y = "Diversity", 
         title = "Alpha diversity of bacterial 
         communities in herbivorous reef fish")
#fig2B + geom_boxplot(aes(colour = black))
#fig2B <- fig2B + theme_bw() + geom_point(size = 2.5, aes(color = Sp)) + 
fig2B
Figure 2B

Figure 2B

pdf("R_OUTPUT/Figure_2B.pdf")
fig2B
invisible(invisible(dev.off()))

Click on the back to diversity tabs to explore correlations with alpha diversity.


\(\beta\)-diversity plots

Beta diversity basically tells us how similar or dissimilar samples are to one another. Phyloseq offers several ordination methods and distance metrics. Here we use non metric multidimensional scaling (NMDS) coupled with Jensen–Shannon divergence. We also save a copy of the figure for later tweaking. To see the full output of the NMDS analysis, remove the results = 'hide' tag from the code chunk.

set.seed(3131)
ord.nmds.jsd_slv <- ordinate(ps_slv_work_filt, method = "NMDS", distance = "jsd")
stressplot(ord.nmds.jsd_slv)

## 
## Call:
## metaMDS(comm = ps.dist) 
## 
## global Multidimensional Scaling using monoMDS
## 
## Data:     ps.dist 
## Distance: user supplied 
## 
## Dimensions: 2 
## Stress:     0.1646318 
## Stress type 1, weak ties
## Two convergent solutions found after 20 tries
## Scaling: centring, PC rotation 
## Species: scores missing

We see that a convergent solution was reached around 20 iterations and our stress is below 0.20, meaning that 2-axes are sufficient to view the data. Generally, we are looking for stress values below 0.2. If the stress values are high, you may need to add more axes to the ordination. Lets visualize the plot.

Figure 2C

fig2C <- plot_ordination(ps_slv_work_filt, ord.nmds.jsd_slv, color = "Sp", 
    label = "SamName", title = "Jensen-Shannon divergence")
fig2C <- fig2C + geom_point(size = 4) + geom_point(shape = 1, 
              size = 3.6, colour = "black", stroke = 0.75) # +  xlim(-0.4, 0.4) + ylim(-0.4, 0.4)
fig2C <- fig2C + scale_colour_manual(values = samp_pal)
fig2C <- fig2C + theme(axis.line = element_line(colour = "black"), panel.background = element_blank(), 
    panel.grid.major = element_line("grey"), panel.grid.minor = element_line("grey"), 
    panel.border = element_rect(colour = "black", fill = NA, size = 1)) + 
    theme(legend.key = element_rect(colour = "black"))
fig2C <- fig2C + coord_fixed()
fig2C <- fig2C  + stat_ellipse(type = "t") + theme_bw()
fig2C
Figure 2C

Figure 2C

pdf("R_OUTPUT/Figure_2C.pdf")
fig2C
invisible(dev.off())

So we can see some clustering within groups and spread between groups, but this is not a test for statistical differences. Do microbial communities differ significantly by host taxa? Click on the back to diversity tabs to see how we employ some statistical tests of beta-diversity.

\(\beta\)-diversity stats

To test whether microbial communities differ by host species we can use permutational analysis of variance (PERMANOVA) or analysis of similarity (ANOSIM). PERMANOVA does not assume normality but does assume equal beta dispersion between groups. We will test beta dispersion below.

First we use the adonis function in vegan to run a PERMANOVA test. This will tell us whether host species have similar centroids or not.

set.seed(1911)
fish.jsd <- phyloseq::distance(ps_slv_work_filt, method = "jsd")
sampledf <- data.frame(sample_data(ps_slv_work_filt))
fish_adonis <- adonis(fish.jsd ~ Sp, data = sampledf, permutations = 1000)
fish_adonis
## 
## Call:
## adonis(formula = fish.jsd ~ Sp, data = sampledf, permutations = 1000) 
## 
## Permutation: free
## Number of permutations: 1000
## 
## Terms added sequentially (first to last)
## 
##           Df SumsOfSqs MeanSqs F.Model      R2   Pr(>F)    
## Sp         4    4.4045 1.10113   16.52 0.59489 0.000999 ***
## Residuals 45    2.9995 0.06665         0.40511             
## Total     49    7.4040                 1.00000             
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

These results indicate that centroids are significantly different across host species meaning that communities are different by host species.

We can also use the pairwiseAdonis package for pair-wise PERMANOVA analysis.

pairwise.adonis(fish.jsd, factors = sampledf$Sp, p.adjust.m = "bonferroni")

Here we see again we see that communities are different by host species.

However, PERMANOVA assumes equal beta dispersion so we will use the betadisper function from the vegan package to calculate beta dispersion values.

beta_adonis <- betadisper(fish.jsd, sampledf$Sp, bias.adjust = TRUE)
beta_adonis
## 
##  Homogeneity of multivariate dispersions
## 
## Call: betadisper(d = fish.jsd, group = sampledf$Sp, bias.adjust =
## TRUE)
## 
## No. of Positive Eigenvalues: 34
## No. of Negative Eigenvalues: 15
## 
## Average distance to median:
##  AcCoe  AcTra  ScTae  SpAur  SpVir 
## 0.2980 0.3098 0.1845 0.1816 0.2570 
## 
## Eigenvalues for PCoA axes:
##  PCoA1  PCoA2  PCoA3  PCoA4  PCoA5  PCoA6  PCoA7  PCoA8 
## 1.8547 1.5977 0.9602 0.7234 0.5659 0.3413 0.2609 0.2427

And then a pair-wise Permutation test for homogeneity of multivariate dispersions using permutest (again from the vegan package).

permutest(beta_adonis, pairwise=TRUE, permutations=1000)
## 
## Permutation test for homogeneity of multivariate dispersions
## Permutation: free
## Number of permutations: 1000
## 
## Response: Distances
##           Df  Sum Sq  Mean Sq      F N.Perm   Pr(>F)   
## Groups     4 0.14600 0.036500 4.1891   1000 0.007992 **
## Residuals 45 0.39209 0.008713                          
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Pairwise comparisons:
## (Observed p-value below diagonal, permuted p-value above diagonal)
##           AcCoe     AcTra     ScTae     SpAur  SpVir
## AcCoe           0.8051948 0.0029970 0.0119880 0.2797
## AcTra 0.8147277           0.0159840 0.0139860 0.2557
## ScTae 0.0040024 0.0160930           0.9440559 0.0420
## SpAur 0.0125449 0.0160815 0.9425028           0.0699
## SpVir 0.2735320 0.2661694 0.0479863 0.0669727

These results are significant, meaning that host species have different dispersions. Looking at the pairwise p-values and permuted p-value, we see that the significant differences (p-value < 0.05) are between:

  • SpAur & AcCoe, AcTra
  • ScTae & AcCoe, AcTra, SpVir

This means we are less confident that the PERMANOVA result is a real result, and that the result is possibly due to differences in group dispersions.

We can also use Analysis of Similarity (ANOSIM)—which does not assume equal group variances—to test whether overall microbial communities differ by host species.

spgroup <- get_variable(ps_slv_work_filt, "Sp")
fish_anosim <- anosim(distance(ps_slv_work_filt, "jsd"), grouping = spgroup)
summary(fish_anosim)
## 
## Call:
## anosim(x = distance(ps_slv_work_filt, "jsd"), grouping = spgroup) 
## Dissimilarity: 
## 
## ANOSIM statistic R: 0.8544 
##       Significance: 0.001 
## 
## Permutation: free
## Number of permutations: 999
## 
## Upper quantiles of permutations (null model):
##    90%    95%  97.5%    99% 
## 0.0556 0.0758 0.0873 0.1111 
## 
## Dissimilarity ranks between and within classes:
##         0%    25%   50%    75% 100%   N
## Between 94 462.50 726.5 977.25 1225 992
## AcCoe   36 185.50 290.5 360.00  582  28
## AcTra   14 162.25 322.0 510.50  789  36
## ScTae    9  34.75  74.5 122.00  419  36
## SpAur    1  30.50  76.0 137.50  562  78
## SpVir   13  95.00 177.0 273.00  584  55

And the AN0SIM result is significant meaning that host species influences microbial community composition.

back to diversity tabs


Part IV: Differentially Abundant ASVs

back to top

At this point we have a good handle on the diversity of the intestinal microbiomes of these herbivorous reef fish. We know that communities are dominated by the same broad-level taxonomic groups. The beta diversity analysis demonstrates that communities partition along host species. Now we want to determine which ASVs are driving these patterns and assess their distribution in nature using publicly available data. To accomplish this task we leave the R environment and employ some additional tools. To summarize, our goals here are to:

  1. identify differentially abundant (DA) ASVs across host species,
  2. find closest database matches to DA ASVs, and
  3. perform phylogenetic reconstruction on DA ASVs and top hits.

Differential abundance (DA)

We used LDA Effect Size (LEfSe) to identify differentially abundant (DA) ASVs across host species and the MicrobiomeAnalyst webserver to run the analysis. There are also many other great tools on the MicrobiomeAnalyst webserver by the way. We needed three files for the input—an ‘OTU’ table, a metadata file containing sample information, and a taxonomy table. We generate these tables with the code below.

########## OTU table
OTU1 <-  as(otu_table(ps_slv_work_filt), "matrix")
# transpose if necessary
# Coerce to data.frame
OTUdf <- as.data.frame(t(OTU1))
setDT(OTUdf, keep.rownames = TRUE)[]
write.table(OTUdf, "R_OUTPUT/seq_tab_for_core.txt", 
    sep = "\t", row.names = FALSE, quote = FALSE)
colnames(OTUdf)[1] <- "#NAME"
write.table(OTUdf, "LEfSe_INPUT/LEfSe_INPUT_seq_tab.txt", 
    sep = "\t", row.names = FALSE, quote = FALSE)
############ TAX Table
# Remember in the `tax_table` we added the last columns as the actual sequence 
# of each ASV and the ASV_ID. We do not need those here.
# So lets only keep the first 6 columns (the taxonomic lineage)
TAX1 <- as(tax_table(ps_slv_work_filt), "matrix")
TAXdf <- as.data.frame(TAX1)
setDT(TAXdf, keep.rownames = TRUE)[]
colnames(TAXdf)[1] <- "#TAXONOMY"
TAXdf <- TAXdf[, 1:6]
write.table(TAXdf, "LEfSe_INPUT/LEfSe_INPUT_tax_tab.txt", 
    sep = "\t", row.names = FALSE, quote = FALSE)
############ Metadata file
meta_file <- data.frame(NAME = sample_name, SampleType = species, Gen = genus)
rownames(meta_file) <- samples.out
colnames(meta_file) <-  c("#NAME", "Species", "Genus" )
# but we still have those three samples that need to be removed
meta_file <- filter(meta_file, Species != "SpChr" & Species != "ScVet")
write.table(meta_file, "LEfSe_INPUT/LEfSe_INPUT_metadata.txt", sep = "\t", row.names = FALSE, 
    quote = FALSE)

And once we have the three files, we head over to the MicrobiomeAnalyst webserver and upload the files. Be sure to select Silva taxonomy in the drop-down menu.
Check the data summary after uploading the files:

  • OTU annotation: SILVA
  • OTU number: 11144
  • OTU with ≥ 2 counts: 4121
  • Sample number: 50
  • Number of experimental factors: 2
  • Total read counts: 2828112
  • Average counts per sample: 56562
  • Maximum counts per sample: 175116
  • Minimum counts per sample: 11568

Cool, all looks good. Hit Proceed. Here are the settings we used for the different step:

  • Filter the data: Minimum count = 20, Prevalence in samples (%) = 20, and Percentage to remove (%) = 0. This removed 3796 low abundant ASVs.
  • Data Normalization: Data rarefying = Do not rarefy my data,
    Data scaling = Total sum scaling (TSS), and
    Data transformation = Do not transform my data.
  • LEfSe analysis Log LDA score = 4 & Adjusted p-value cutoff = 0.0001. We specifically chose these values because we found that they eliminated spurious results such as DA ASVs that were really abundant in a few samples but not consistent across an entire group.

The result was 59 differentially abundant (DA) ASVs.


Results of LEfSe analysis

We can inspect and save the results of the LEfSe analysis. The table shows the Linear discriminant analysis (LDA) scores, P-values adjusted for multiple testing, and False Discovery Rate (FDR) values from the LEfSe analysis. Normalized read abundance values for each host species are also given.

Supplementary Table 5

lefse_tab <- read.table("MANUAL_INPUT/lefse_results.txt", header = TRUE, 
    sep = "\t", check.names = FALSE)
write.table(lefse_tab, "R_OUTPUT/Supplementary_Table_5.txt", quote = FALSE, sep = "\t", row.names = FALSE)
datatable(lefse_tab, rownames = FALSE, width = "100%", caption = 
    htmltools::tags$caption(style = "caption-side: bottom; text-align: left;", 
    "Table 4: ", htmltools::em("Results of LEfSe analysis.")), extensions = "FixedColumns", 
    "Buttons", options = list(columnDefs = list(list(className = "dt-center", 
        targets = c(1, 2, 3, 4, 5, 6, 7, 8))), dom = "Blfrtip", pageLength = 5, 
        lengthMenu = c(5, 10, 25, 60), buttons = c("csv", "copy"), scrollX = TRUE, 
        scrollCollapse = TRUE))

Core microbiome

Before getting knee deep in the DA ASV analysis lets see if we can’t identify some core elements, or ASVs, to these fish. First we need a mothur-formatted .shared file.

cm <- read.table("R_OUTPUT/seq_tab_for_core.txt", sep = "\t", header = TRUE, row.names=1)

cm_t <- t(cm) 
class(cm_t)
## [1] "matrix"
cm_df <- as.data.frame(cm_t)
class(cm_df)
## [1] "data.frame"
numcols <- ncol(cm_df)
cm_df <- cm_df %>% tibble::rownames_to_column("Group")
cm_df <- cm_df %>% mutate(label = 0.03, numOtus = numcols) %>% select(label, Group, numOtus, everything())
write.table(cm_df, "R_OUTPUT/ps_slv_work_filt.txt", quote = FALSE, sep = "\t", row.names = FALSE)

####COMBINE by fish species

cm_Merge <- cm %>% tibble::rownames_to_column("ASV")

cm_Merge <- cm_Merge %>% mutate(AcCoe = 
                                    AcCoe01+AcCoe02+AcCoe03+AcCoe04+AcCoe05+
                                    AcCoe06+AcCoe07+AcCoe08)
cm_Merge <- cm_Merge %>% mutate(AcTra = 
                                    AcTra01+AcTra02+AcTra03+AcTra04+AcTra05+
                                    AcTra06+AcTra07+AcTra08+AcTra09)
cm_Merge <- cm_Merge %>% mutate(ScTra = 
                                    ScTae01+ScTae02+ScTae03+ScTae04+ScTae05+
                                    ScTae06+ScTae07+ScTae08+ScTae09)
cm_Merge <- cm_Merge %>% mutate(SpAur = 
                                    SpAur01+SpAur02+SpAur03+SpAur04+SpAur05+
                                    SpAur06+SpAur07+SpAur08+SpAur09+SpAur10+
                                    SpAur11+SpAur12+SpAur13)
cm_Merge <- cm_Merge %>% mutate(SpVir = 
                                    SpVir01+SpVir02+SpVir03+SpVir04+SpVir05+
                                    SpVir06+SpVir07+SpVir08+SpVir09+SpVir10+
                                    SpVir11)

cm_Merge <- cm_Merge %>% select(ASV, AcCoe, AcTra, ScTra, SpAur, SpVir)
cm_Merge_2 <- cm_Merge[,-1]
rownames(cm_Merge_2) <- cm_Merge[,1]

cm_Merge_2_t <- t(cm_Merge_2)
class(cm_Merge_2_t)
## [1] "matrix"
cm_Merge_2_df <- as.data.frame(cm_Merge_2_t)
class(cm_Merge_2_df)
## [1] "data.frame"
cm_Merge_2_df <- cm_Merge_2_df %>% tibble::rownames_to_column("Group")
cm_Merge_2_df <- cm_Merge_2_df %>% mutate(label = 0.03, numOtus = numcols) %>% select(label, Group, numOtus, everything())
write.table(cm_Merge_2_df, "R_OUTPUT/ps_slv_work_filt_combine.txt", quote = FALSE, sep = "\t", row.names = FALSE)

Next we use the output to run get.coremicrobiome in mothur.


Searching public databases

Next we wanted to know where else these ASVs had been detected in nature. There is a huge wealth of publicly available sequence information from many studies and habitats. We can use this information to get a better idea of the distribution and habitat specificity of the DA ASVs. To accomplish this we performed the following steps:

  1. First we needed a phyloseq object that only contained the DA ASVs. To do this, we passed an object consisting of just these 59 ASVs (from the LEfSe analysis) to the phyloseq function prune_taxa. We needed two different ps objects, one from the unmerged object (ps_slv_work_filt) and the other from the merged-by-genus object (mergedGP).

  2. Next we needed a fasta file of our DA ASVs. We could not find an easy way to export a fasta file from the new ps objects. So we tried this using the tax_table. This approach works but, well, it is not very elegant. If you want a fasta file from any other ps objects just swipe out the name of the ps object in the code below. Anyway, we will generate and save a fasta file.

Please note that on the mac we used to analyze the data, for some reason, saves the fasta file with Line Break Type as Legacy Mac(CR). This may be incompatible with other programs and needs to be changed to UNIX (LS).
# Object of DA ASVs
lefse_asvs <- c("ASV21", "ASV25", "ASV35", "ASV44", "ASV159", "ASV17", 
    "ASV174", "ASV22", "ASV234", "ASV60", "ASV114", "ASV268", "ASV14", 
    "ASV226", "ASV23", "ASV29", "ASV30", "ASV48", "ASV90", "ASV98", "ASV18", 
    "ASV41", "ASV7", "ASV43", "ASV5", "ASV54", "ASV8", "ASV9", "ASV250", 
    "ASV12", "ASV32", "ASV34", "ASV39", "ASV224", "ASV398", "ASV127", "ASV128", 
    "ASV151", "ASV323", "ASV359", "ASV374", "ASV91", "ASV56", "ASV6", "ASV165", 
    "ASV284", "ASV395", "ASV450", "ASV15", "ASV2", "ASV20", "ASV298", "ASV57", 
    "ASV69", "ASV75", "ASV82", "ASV1", "ASV70", "ASV49")
# Create ps objects
da_asvs <- prune_taxa(lefse_asvs, mergedGP)
da_asvs_full <- prune_taxa(lefse_asvs, ps_slv_work_filt)
# Create fasta file from tax_table
table2format <- tax_table(da_asvs)
#retain only the column with the sequences
table2format_trim <- table2format[, 7]  
table2format_trim_df <- data.frame(row.names(table2format_trim), table2format_trim)
colnames(table2format_trim_df) <- c("ASV_ID", "ASV_SEQ")
table2format_trim_df$ASV_ID <- sub("ASV" , ">ASV", table2format_trim_df$ASV_ID) #format fasta
write.table(table2format_trim_df, "R_OUTPUT/ASV_FOR_BLAST.fasta", sep = "\r", 
    col.names = FALSE, row.names = FALSE, quote = FALSE, fileEncoding = "UTF-8")
  1. With our newly created DA ASV fasta file we can move on to database searching. We used BLASTn against the nr database to search publicly available sequence data. We used these settings:
    • optimized for highly similar sequences (megablast)
    • Expect threshold = 10
    • Word size = 28
    • Match/Mismatch Scores = 1, -2
    • Gap Costs = linear.
    • retain top 10 hits

Here are the top BLAST hits for each DA ASV. The table displays a lot of information about each BLAST search (it scrolls along the x-axis by the way). Most importantly are the accession numbers of top BLAST hits (subject acc.var), number of 100% identical matches (num perfect hits), the percent identity, and some info on where/when the hit sequence was originally found. Where applicable, there is also PubMedIDs so you can find the paper that reported the sequence. Looking at this table will give you a preliminary sense of the ecology of these ASVs. For example, most hits come from intestinal communities, many of which are marine herbivorous fish. But the low percent identity of several ASVs indicates that these sequences have been poorly sampled. This is not surprising given the geographic skew of sampling.


Top hits from BLASTn analysis

Note This table also scrolls horizontally.

Supplementary Table 6

blast_tab <- read.table("MANUAL_INPUT/BLAT_results.txt", header = TRUE, 
    sep = "\t", check.names = FALSE)
write.table(blast_tab, "R_OUTPUT/Supplementary_Table_6.txt", row.names = FALSE, sep = "\t", quote = FALSE)
datatable(blast_tab, rownames = FALSE, caption = htmltools::tags$caption(style = "caption-side: bottom; text-align: left;", 
    "Supplementary Table 6: ", htmltools::em("Results of BLASTn analysis.")), extensions = "FixedColumns", 
    "Buttons", options = list(columnDefs = list(list(className = "dt-center", 
        targets = c(1, 2, 3, 4, 5, 6, 7, 8))), dom = "Blfrtip", pageLength = 5, 
        lengthMenu = c(5, 10, 25, 65), buttons = c("csv", "copy"), scrollX = TRUE, 
        scrollCollapse = TRUE))

Several ASVs returned more than one match at 100%. For some ASVs, the 100% matches were from the same study/study organism, so we just selected one as the representative. ASVs 6, 12, 224, and 398 returned numerous 100% matches (out of 50 total). These data were impractical to summarize and not very informative anyway. So we elected to leave these data out. If you want to see what these hits are, just grab the ASV sequence and BLAST away.

Also, some ASVs shared the same to hit. Since the table is ‘by ASV’ we retained all duplicate hits.

  1. For phylogenetic inference we used the Silva Alignment, Classification and Tree Service to obtain neighbors of DA ASVs. We used these settings:
    • Search and classify: min identity = 0.95; Number of neighbors = 5.
    • Default parameters for the remainder of the workflow.
  2. We then combined the results of the BLASTn and Silva ACT analyses and omitted duplicate hits, resulting in 297 top hit sequences for phylogenetic analysis.

Phylogenetic inference

Short read sequences are not ideal for phylogenetic analysis, but given the data we have, we felt this was a good place to start. This analysis required several steps.

  1. We used mothur (v.1.40.4, Last updated: 07/25/2018) and the Silva full length sequences and taxonomy references (release 132) to align the DA ASVs and our new reference db, and also classify the sequences. We used a hard mask to trim all long reads to the same length (~373bp) and 16S region as the ASVs.
mothur "#align.seqs(candidate=sequence_tree.fasta, template=silva.nr_v132.align, processors=20, flip=t)"  
mothur "#filter.seqs(fasta=sequence_tree.fasta, vertical = =F, hard=mask.txt)"  
mothur "#classify.seqs(fasta=sequence_tree.filter.fasta, template=silva.nr_v132.align, taxonomy=silva.nr_v132.tax, processors=10)"  
  1. Now that we had our 59 DA ASVs and the top database hits, its was time for phylogenetic inference. We used RAxML-HPC to generate a phylogenetic tree (with Aquifex as the outgroup)
raxmlHPC-PTHREADS-SSE3 -T 24 -f a -p 2345 -x 3456 -m GTRGAMMA  -N 1000 -s sequence_tree.filter.fasta -n fish_arb_align_1000BS_B.tre

Part V: Synthesis

back to top

Now it was time to put the pieces together and determine…

What these patterns tell us about specificity?

Visualizing the phylogenetic tree

IMNGS analysis

To dig just a little deeper, we screened our DA ASVs against the IMNGS database. IMNGS hosts a curated database of short-read sequences scraped from the International Nucleotide Sequence Database Collaboration (GenBank, DDBJ and EMBL). The database is rebuilt monthly and at the time of this analysis, contained 271,237 samples. IMNG is really designed to screen full-length 16S rRNA sequences and is not ideal for shorter reads. This is because the database is built from short reads, and different studies target different regions of the gene. For example, if we get no hits to an ASV it could mean the organisms it came from has really not been detected before or that it is in the database but is represented by a different 16S region. So take these data with a grain of salt.

IMNGS is not a high-throughput system. User may only submit a maximum of 10 sequences per query and this can (and will) take weeks to run. So choose your ASVs carefully.

IMNGS will return a lot of useful data for each query sequence. All we were interested in here was the number of hits, but there much more really useful data here. Among other data products, IMNGS returns report tables that tally the number of samples that were positive for the presence of query-like sequences for each sample category—categories like shrimp gut metagenome and seawater metagenome. Each category has a number of short-read samples, which could originate from a single study or multiple studies.

A report includes values for several percent identity cutoff values. We set a minimum threshold at 97% so our reports have values for 97 and 99%. You can set the threshold as low as 90%.

IMNGS provides three such reports based on the abundance of your query sequence.

  1. An SRA-derived sample is considered positive if the query-like sequences sum up to more than 0% of the total number of sequences in that sample (i.e. any abundance).

  2. An SRA-derived sample is considered positive if the query-like sequences sum up to more than 0.1% of the total number of sequences in that sample (i.e. excluding rare abundances).

  3. An SRA-derived sample is considered positive if the query-like sequences sum up to more than 1% of the total number of sequences in that sample (i.e. including only dominant OTUs).

We report data from 97% cutoff identity and 0.1% of total reads in a sample. I think this is a little confusing so let me explain by example. In the tree below, ASV398 is most closely related to an Alphaproteobacteria associated with the toxic benthic marine dinoflagellate, Ostreopsis ovata. We retrieved this sequence during the BLASTn analysis discussed above. Anyway, ASV398 was screened against IMNGS and returned 2460 hits. This means that at 97% identity, 2460 samples had an ASV398-like sequence comprising greater than 0.1% of a given samples total number of sequences. If for example we increase the percent identity to 99% the number of sample hits drops to 138. If instead we look at the 0% report (97% identity), the number of sample hits increases to 6323.

Sullam lifestyle categories

We also compared the final list of top hits to the Sullam et. al. paper, specifically Table S1 from that paper. Because this paper was published in 2012, there were many sequence hits in our db that did not appear in the original paper. However, for those that did, we added the Sullam lifestyle category designations to the tree metadata.

Putting the pieces together

We took all of these data and used iTOL to visualize the tree. For each top hit, we added the isolation source/natural host information, taxonomic affiliation, and Sullam lifestyle category. We also overlaid the number of hits to the IMNGS database for each ASV.

To view a full, interactive version of the tree go this iTOL page.


Zoomable iTOL tree

Figure 3


Inferring lifestyle category

We used the tree to infer the lifestyle category of each ASVs based on the closest relatives in their clade. This was not a quantitative, but rather a user guided determination. Aside from MetaMetaDb (discussed above) we are not aware of any tool currently available to quantitatively assess habitat preference of a 16S rRNA sequence.

For simplicity we focused on three lifestyle catagories (though we have seven categories in the tree). Our reasoning—again based on the work of Sullam et. al.—was that fish intestines contain microbes that come from the environment (what they eat, where they live), microbes that are there because fish are animals with guts, and microbes that are there because fish are fish and have a physiology and evolutionary history that select specific organisms. Sullam et.al. were also looking at fish from different habitats (freshwater, estuary, marine) and tropic levels (carnivores, herbivores, omnivores) while our study was more narrow in scope.

  • fish associated: ASVs most closely related to sequences from the intestinal tract of marine fish.
  • animal associated: ASVs most closely related to sequences from other animals, including one freshwater fish, other vertebrates, and a few non-marine invertebrates.
  • environmental: ASVs most closely related to sequences from the environment. By and large these are marine or marine-like (e.g., hypersaline mats, saline lakes) in origin including sediments, water, and potential prey (algae, plants, coral, sponge). However there are some leaves in the tree from non-marine environments (e.g., activated sludge) that we grouped in the environmental category.

Assessing habitat specificity.

We combined these habitat predictions with the results of the BLASTn analysis, scan of the IMNGS database, Sullam lifestyle categories, etc. and put it all in one editable table. So if you disagree with a habitat prediction, you can feel free to change it.

Description of table headings

  • ASV: ASV id.
  • Putative habitat: Our habitat designation based on the analyses.
  • Enriched: Which of the five herbivorous reef fish the ASV was enriched in.
  • Taxon: Taxonomic classification of the ASV.
  • closest db match: Host or environment of top hit.
  • % identity: Percent identity of top hit.
  • subject acc Accession number of top BLAST hit
  • IMNGS hits: Number of hits to the IMNGS database. Value indicates the number of samples that scored a hit to an ASV.
  • Sullam lifestyle: Lifestyle category as defined by Sullam et. al., 2012. NLC indicates no hit to the Sullam db.
  • num perfect hits Number of 100% BLAST matches out of 50 top hits.

Note This table also scrolls horizontally.

habi_tab <- read.table("MANUAL_INPUT/habitat_specificity.txt", header = TRUE, 
    sep = "\t", check.names = FALSE)
habi_tab2 <- habi_tab[order(habi_tab$habitat_code, habi_tab$Enriched), ] # order by habitat and host enriched
#habi_tab <- habi_tab[, -2] #delete code column
datatable(habi_tab2, rownames = FALSE, colnames = c(
             "ASV", "Habitat code", "Putative habitat", "Enriched", 
             "Taxon", "Closest db match", "Per identity", "Subject acc", 
             "IMNGS hits", "Sullam lifestyle", "Num perfect hits"),
          editable = TRUE, 
          caption = htmltools::tags$caption(style = "caption-side: bottom; text-align: left;", 
                    "Table 6: ", htmltools::em("Assessing habitat specificity.")), 
          extensions = "Buttons", 
          options = list(columnDefs = list(list(className = "dt-center", 
          targets = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))), 
          dom = "Blfrtip", pageLength = 5, lengthMenu = c(5, 10, 25, 60), 
          buttons = c("csv", "copy"), scrollX = TRUE, scrollCollapse = TRUE))
write.table(habi_tab2, "R_OUTPUT/habitat_specificity.txt", sep = "\t", 
    col.names = TRUE, row.names = TRUE, quote = FALSE, fileEncoding = "UTF-8")

NR Indicates Not Recorded. Four ASVs had numerous hits at 100% identity. We did not include top hit data for these ASVs.


Summary of habitat diversity

Now we can summarize the data for each lifestyle category. This table was constructed in a text file and read into R.

Table 1

habi_summary <- read.table("MANUAL_INPUT/Table_1.txt", header = TRUE, 
    sep = "\t", check.names = FALSE)
datatable(habi_summary, rownames = FALSE, editable = TRUE, 
          caption = htmltools::tags$caption(style = "caption-side: bottom; text-align: left;", 
                    "Table 1: ", htmltools::em("Summary of habitat specificity.")), 
          extensions = "Buttons", 
          options = list(columnDefs = list(list(className = "dt-center", 
          targets = c(1, 2, 3, 4, 5))), 
          dom = "Brti", 
          buttons = c("csv", "copy"), scrollX = TRUE, scrollCollapse = TRUE))

So we know that Host X is enriched for some ASV from taxa Y. Is this part of a larger pattern or an isolated case? For a given taxonomic group and rank, what proportion of total reads (from all ASVs) were found in a particular host species? At some point it would be nice if this were an interactive step, but for now we must modify the code below to look at different taxa. This example will look at the family Desulfovibrionaceae (Deltaproteopbacteria)

Proportion of total reads for a given taxon & rank

calc_tax_prop <- subset_taxa(mergedGP, Family == "Desulfovibrionaceae") # Change this to select different taxa
calc_tax_prop
## phyloseq-class experiment-level object
## otu_table()   OTU Table:         [ 71 taxa and 5 samples ]
## sample_data() Sample Data:       [ 5 samples by 3 sample variables ]
## tax_table()   Taxonomy Table:    [ 71 taxa by 8 taxonomic ranks ]
sample_sums_by_taxa <- sample_sums(calc_tax_prop)

total_taxa_reads <- sum(sample_sums_by_taxa)
sample_sums_by_taxa <- as.data.frame(sample_sums_by_taxa)

sample_sums_by_taxa$proportion <- 
                              (sample_sums_by_taxa$sample_sums_by_taxa/total_taxa_reads) * 100
colnames(sample_sums_by_taxa) <- c("total taxa reads", "Proportion")
sample_sums_by_taxa$Proportion <- round(sample_sums_by_taxa$Proportion, digits = 2)
total_taxa_reads
## [1] 206831
sample_sums_by_taxa

Great. Looks like there are 71 Desulfovibrionaceae ASVs and the majority (> 90%) of the reads are from Acanthurus. This is interesting. We can do this with any taxa we wish.

Also, for the record, here is the proportion of Cyanobacteria reads by host species. We used the code above and the original phyloseq object before removing the Cyanobacteria. There were a total of 1074 ASVs.

Host species total taxa reads Proportion
AcCoe 117909 53.27
AcTra 43261 19.54
ScTae 31013 14.01
SpAur 14534 6.57
SpVir 14636 6.61

At this point we know which ASVs are enriched in which host species, the lineage of those ASVs, and something about where else these sequences have been detected in nature. Next we would like to know the proportion of total reads for each ASV that is found in each host species. We start with a summary table of these data.

Proportion of total ASV reads per host species

# calculate the averages and merge by species
# grab the da_asv ps object & merge by samples
daASV_mergedGP_BAR <- merge_samples(da_asvs_full, "Sp")
#daASV_SD_BAR <- merge_samples(sample_data(da_asvs_full), "Sp")
# calculate percent proportion
daASV_AVG <- apply(t(otu_table(daASV_mergedGP_BAR)), 1, function(x) x/sum(x)) 
# transpose
daASV_t_AVG <- t(daASV_AVG)
daASV_t_AVG_df <- as.data.frame(daASV_t_AVG)

######################
da_ASV_tax <- habi_tab[c("ASV", "Taxon", "Putative_habitat")] # choose columns of interest
da_ASV_tax2 <- da_ASV_tax[,-1]
rownames(da_ASV_tax2) <- da_ASV_tax[,1]
######################

######################
# combine based on ASV column
daASV_work <- merge(daASV_t_AVG_df, da_ASV_tax2, by = 0, all = TRUE)
rownames(daASV_work) <- daASV_work[,1]
daASV_work$Row.names <- NULL
# then make column row.names
daASV_work2 <- cbind(ASV = rownames(daASV_work), daASV_work)
#melt the df
daASV_work3 <- melt(daASV_work2, value.name = "ASV") # wide to long format?
colnames(daASV_work3) <- c("ASV", "Taxon", "Putative_habitat", "Sample", "Proportion")
daASV_work3$Proportion <- round(daASV_work3$Proportion, digits = 5)

datatable(daASV_work3, rownames = TRUE, editable = FALSE, 
          caption = htmltools::tags$caption(style = "caption-side: bottom; text-align: left;", 
                    "Table 8: ", htmltools::em("DA ASV sample proportion.")), 
          extensions = "Buttons", 
          options = list(columnDefs = list(list(className = "dt-center", 
          targets = c(1, 2, 3, 4, 5))), 
          dom = "Blfrtip", pageLength = 5, lengthMenu = c(5,10, 50, 100, 300), 
          buttons = c("csv", "copy"), scrollX = TRUE, scrollCollapse = TRUE))
#write.table(daASV_work3, "R_OUTPUT/Table_18.txt", 
#            sep = "\t", row.names = FALSE, quote = FALSE)

Now that we have a list of DA ASVs and their assigned habitat preference, we want to create a R object that organizes these in some logical fashion. We can then use this object to order subsequent graphs and tables. So lets order the ASVs by putative habitat preference and then by the host species in which that ASV was enriched. Seems reasonable enough? Depending on the R command, some objects need to be in ascending order, others in descending order.

asv_order <- c("ASV450", "ASV165", "ASV395", "ASV284", "ASV56", "ASV6", 
               "ASV359", "ASV128", "ASV127", "ASV91", "ASV374", "ASV151", 
               "ASV323", "ASV398", "ASV224", "ASV39", "ASV34", "ASV12", 
               "ASV32", "ASV250", "ASV43", "ASV54", "ASV9", "ASV5", "ASV49", 
               "ASV8", "ASV41", "ASV18", "ASV7", "ASV90", "ASV29", "ASV98", 
               "ASV23", "ASV30", "ASV226", "ASV48", "ASV70", "ASV1", "ASV14", 
               "ASV298", "ASV82", "ASV75", "ASV69", "ASV57", "ASV20", "ASV15", 
               "ASV2", "ASV268", "ASV114", "ASV234", "ASV174", "ASV60", "ASV17", 
               "ASV22", "ASV159", "ASV44", "ASV25", "ASV21", "ASV35")
asv_order_rev <- rev(asv_order)

Lets see if we can overlay all of this information in one “easy” to understand plot. The first thing is to do is plot the proportion of reads for a given ASV from each host species.

Graphical representation of proportion of total ASV reads per host species

daASV_work3$ASV <- as.character(daASV_work3$ASV)
daASV_work3$ASV <- factor(daASV_work3$ASV, levels = unique(daASV_work3$ASV))
daASV_work3$ASV <- factor(daASV_work3$ASV, levels= asv_order)

Next, we created a bar plot of read proportion by host species for each ASV. And save a copy to the FIGURES/ directory.

Code for proportional bar chart

#Bar charts
ASV_bar <- ggplot(daASV_work3, aes_string(x = "ASV", 
      y = "Proportion", fill = "Sample"), environment = .e, ordered = TRUE, xlab = "x-axis label", 
      ylab = "y-axis label") 
ASV_bar <- ASV_bar + geom_bar(stat = "identity", position = position_stack(reverse = TRUE), 
    width = 0.95) + coord_flip() + theme(aspect.ratio = 2/1)
ASV_bar <- ASV_bar + scale_fill_manual(values = samp_pal)
ASV_bar <- ASV_bar + theme(axis.text.x = element_text(angle = 0, hjust = 0.95, 
    vjust = 1))
ASV_bar <- ASV_bar + guides(fill = guide_legend(override.aes = list(colour = NULL), 
    reverse = FALSE)) + theme(legend.key = element_rect(colour = "black"))
ASV_bar <- ASV_bar + labs(x = "Host species", y = "Proportion (% total reads)", 
    title = "ASV Proportion by host species")
ASV_bar <- ASV_bar + theme(axis.line = element_line(colour = "black"), panel.grid.major = element_blank(), 
    panel.grid.minor = element_blank(), panel.border = element_rect(colour = "black", 
        fill = NA, size = 1))
ASV_bar

pdf("R_OUTPUT/bar_plot_read_prop.pdf")
ASV_bar
invisible(dev.off())

Code for heatmap

# Heatmap
library(ComplexHeatmap)
library(circlize)
library(heatmap3)
library(gdata)
fig4_heat <- as.data.frame(t(otu_table(da_asvs)))
fig4_tax <- as.data.frame(habi_tab2) # Convert habi_table to df and store in new variable
fig4_tax_tab <- fig4_tax[, -1] # eliminate 1st column so can combine based on row names
rownames(fig4_tax_tab) <- fig4_tax[, 1] # Make new row.names from original table
fig4_tax_tab <- fig4_tax_tab[c(4,2,3,1,5,6,7,8)] # Reorder
fig4_tax_tab <- fig4_tax_tab[c(1:4)] # Select columns

# Combine the two df by rowname
# If the matching involved row names, an extra character column called Row.names 
#is added at the left, and in all cases the result has ‘automatic’ row names.
fig4_heatmap2 <- merge(fig4_tax_tab, fig4_heat, by = 0, all = TRUE) 

fig4_heatmap <- subset(fig4_heatmap2, select=-c(Row.names))
rownames(fig4_heatmap) <- fig4_heatmap2[,'Row.names']
fig4_heatmap <- cbind(ASV = rownames(fig4_heatmap), fig4_heatmap) # make rownames a column

fig4_heatmap$ASV <- factor(fig4_heatmap$ASV, levels=rev(asv_order)) 
fig4_heatmap <- fig4_heatmap[order(fig4_heatmap$ASV),] 

fig4_heatmap$ID <- paste(fig4_heatmap$ASV, fig4_heatmap$Taxon, fig4_heatmap$Putative_habitat, fig4_heatmap$Enriched, sep = "_") # combine the columns to make one name
fig4_heatmap2 <- fig4_heatmap[-c(1:5)] # delete the original columns
fig4_heatmap2 <- fig4_heatmap2[c(6,1,2,3,4,5)] # reorder
rownames(fig4_heatmap2) <- fig4_heatmap2[, 1]
fig4_heatmap2 <- fig4_heatmap2[-1]

Figure 4

####Define Colors
taxa_colors <- unlist(lapply(row.names(fig4_heatmap2), function(x){
  if(grepl
     
     # environmental
               ("Alphaproteobacteria", x)) '#000000' 
  else if(grepl("Pirellulaceae", x)) '#000000' 
  else if(grepl("Rubritaleaceae", x)) '#000000' 
  else if(grepl("Flavobacteriaceae", x)) '#000000' 
    
   # fish/animal
  else if(grepl("Desulfovibrionaceae", x)) '#0072b2' 
  else if(grepl("Lachnospiraceae", x)) '#f0e442' 
  else if(grepl("Erysipelotrichaceae", x)) '#009e73' 
  else if(grepl("Ruminococcaceae", x)) '#e69f00'
  else if(grepl("Bacteroidales", x)) '#d55e00'
  else if(grepl("Fusobacteriaceae", x)) '#56b4e9'

  # Undertermined
  else if(grepl("Vibrionaceae", x)) '#cc79a7'
  
  # other
  else if(grepl("Family_XIII", x)) '#808080'
  else if(grepl("Mollicutes", x)) '#808080' 
  else if(grepl("Brevinemataceae", x)) '#808080'
  else if(grepl("Peptostreptococcaceae", x)) '#808080'
}))    

habitat_colors <- unlist(lapply(row.names(fig4_heatmap2), function(x){
  if(grepl
               ("fish", x)) '#808080'
  else if(grepl("animal", x)) '#000000'
  else if(grepl("environmental", x)) '#808080'
  else if(grepl("undetermined", x)) '#000000'
}))    
heatColors <- cbind(taxa_colors, habitat_colors)
colnames(heatColors)[1] <- "Taxa"
colnames(heatColors)[2] <- "Habitat"
col <- colorRampPalette(bias = 1, c("#000033", "#66CCFF"))(16)

### Save heatmap
pdf(file = "R_OUTPUT/Figure_4.pdf")
heatmap3(fig4_heatmap2, cexRow=0.5, cexCol=1, 
         margins = c(3,13), RowSideColors=heatColors, scale="row", Colv = NA, 
         Rowv = NA, revC = TRUE, balanceColor = FALSE, col = col)
invisible(dev.off())
heatmap3(fig4_heatmap2,cexRow=0.5, cexCol=1, 
         margins = c(3,13), RowSideColors=heatColors, scale="row", Colv = NA, 
         Rowv = NA, revC = TRUE, balanceColor = FALSE, col = col)


Appendix A: Other analyses & visualizations

back to top

Here is code for other representation of taxa abundance. These are raw R images that have not been gussied up.

We will create two different representations of relative abundance for each sample (arranged by host species) by major Classes—facet grid box-and-whisker plots and bar charts. We will generate each separately (and save) using an earlier relative abundance phyloseq object and then display the combined output.

Code for box-and-whisker plot.

mdata_phy_all <- tax_glom(ps_slv_filt_AVG, taxrank = "Class", NArm = FALSE)
# You can choose any taxonomic level here
mdata_phyrel_all <- transform_sample_counts(mdata_phy_all, function(x) x/sum(x))
meltd_all <- psmelt(mdata_phyrel_all)
meltd_all$Class <- as.character(meltd_all$Class)

means <- ddply(meltd_all, ~Class, function(x) c(mean = mean(x$Abundance)))
taxa_means <- means[order(-means$mean), ]  # decending order
taxa_means <- format(taxa_means, scientific = FALSE)  # ditch the sci notation 

Other <- means[means$mean <= 0.027, ]$Class  # Here we conglomerate at 2%.

meltd_all[meltd_all$Class %in% Other, ]$Class <- "Other"
samp_names <- aggregate(meltd_all$Abundance, by = list(meltd_all$Sample), 
    FUN = sum)[, 1]
.e <- environment()
meltd_all[, "Class"] <- factor(meltd_all[, "Class"], sort(unique(meltd_all[, 
    "Class"])))
meltd_all <- meltd_all[order(meltd_all[, "Class"]), ]
levels(meltd_all$Class)
meltd_all$Class <- factor(meltd_all$Class, levels = c("Bacteroidia", "Clostridia", 
    "Erysipelotrichia", "Fusobacteriia", "Alphaproteobacteria", "Deltaproteobacteria", 
    "Gammaproteobacteria", "Planctomycetacia", "Other"))  # Here we order Classes by the Phylum they belong to.

sup_fig1 <- qplot(data = meltd_all, x = Sp, y = Abundance, fill = Class, 
    geom = "boxplot", ylab = "Relative Abundance") + theme(legend.position = "bottom") + 
    facet_grid(Class ~ ., scales = "free_y", space = "free_y") + geom_jitter(width = 0.05) + 
    geom_point(colour = "black", fill = "white")  #+ guides(guide_legend(reverse = FALSE) )
sup_fig1 <- sup_fig1 + scale_fill_manual(values = friend_pal) + labs(x = "Host species", 
    y = "Relative abundance (% total reads)")
sup_fig1

pdf("R_OUTPUT/box-and-whisker.pdf")
sup_fig1
invisible(dev.off())

Code for bar plot.

Supplementary Figure 1

sup_fig2 <- ggplot(meltd_all, aes_string(x = "Sample", y = "Abundance", 
    fill = "Class"), environment = .e, Ordered = TRUE)
sup_fig2 <- sup_fig2 + geom_bar(stat = "identity", position = "stack") + 
    facet_grid(Class ~ Sp, scales = "free", space = "free")
sup_fig2 <- sup_fig2 + scale_fill_manual(values = friend_pal)

# sup_fig2 <- sup_fig2 + theme(axis.text.x = element_text(angle = -90,
# hjust = 0))
sup_fig2 <- sup_fig2 + theme(axis.text.x = element_blank())
sup_fig2 <- sup_fig2 + guides(fill = guide_legend(override.aes = list(colour = NULL), 
    reverse = FALSE)) + theme(legend.key = element_rect(colour = "black"), 
                              legend.position = "bottom") + 
    labs(x = "Individual samples", y = "Relative abundance (% total reads)")
sup_fig2
Supplementary Figure 1. Relative abundance of major Classes by sample

Supplementary Figure 1. Relative abundance of major Classes by sample

pdf("R_OUTPUT/Supplementary_Figure_1.pdf")
sup_fig2
invisible(dev.off())

return to Figure 2A

Appendix B: Tools & resources used in this workflow

back to top

Specific tools

phyloseq as the primary analytical package.
LEfSe to identify differentially abundant (DA) amplicon sequence variants (ASV) across host fish species.
MicrobiomeAnalyst to conduct LEfSe analysis.
BLASTn and Silva ACT to identify closest hits to DA ASVs.
RAxML for phylogenetic inference of DA ASVs and closest hits.
iTOL for visualization of tree and associated metadata.

Other valuable resources

R Markdown: The Definitive Guide
knitr tutorials. Fantastic site!
Microbiota analysis in R, UCR Workshop 2018 nicely documented workflow with examples.

Appendix C: Submitting sequencing data to public archives

back to top

It is now time to submit the data to your favorite sequence read archive. We submitted out data to the European Nucleotide Archive (ENA). The ENA does not like RAW data and prefers to have primers removed. So we submitted the trimmed Fastq files to the ENA. You can find these data under the study accession number PRJEB28397. The RAW files on our figshare site. TODO INSERT DOI/LINK

To submit to the ENA you need two data tables (plus your sequence data). The first file describes the samples and the second file describes the sequencing data. The original files can be found on the figshare site.

Appendix D: Specific R package & versions

back to top

Below are the specific packages and versions used in this workflow using both sessionInfo() and devtools::session_info().

proc.time() - ptm
##      user    system   elapsed 
## -8062.672  -117.683 -3055.536
sessionInfo()
## R version 3.5.0 (2018-04-23)
## Platform: x86_64-apple-darwin15.6.0 (64-bit)
## Running under: macOS High Sierra 10.13.6
## 
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/3.5/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
##  [1] grid      stats4    parallel  stats     graphics  grDevices utils    
##  [8] datasets  methods   base     
## 
## other attached packages:
##  [1] gdata_2.18.0                heatmap3_1.1.1             
##  [3] circlize_0.4.4              ComplexHeatmap_1.19.1      
##  [5] bindrcpp_0.2.2              ggthemes_4.0.1             
##  [7] pairwiseAdonis_0.0.1        cluster_2.0.7-1            
##  [9] plotly_4.8.0                RCurl_1.95-4.11            
## [11] bitops_1.0-6                svgPanZoom_0.3.3           
## [13] gridExtra_2.3               forcats_0.3.0              
## [15] stringr_1.3.1               dplyr_0.7.6                
## [17] purrr_0.2.5                 readr_1.1.1                
## [19] tidyr_0.8.1                 tibble_1.4.2               
## [21] tidyverse_1.2.1             formatR_1.5                
## [23] pander_0.6.2                rmarkdown_1.10             
## [25] DT_0.4                      data.table_1.11.4          
## [27] kableExtra_0.9.0            knitr_1.20                 
## [29] rstudioapi_0.7              reshape2_1.4.3             
## [31] scales_1.0.0                vegan_2.5-2                
## [33] lattice_0.20-35             permute_0.9-4              
## [35] plyr_1.8.4                  ggplot2_3.0.0              
## [37] phyloseq_1.25.2             ShortRead_1.39.0           
## [39] GenomicAlignments_1.17.3    SummarizedExperiment_1.11.6
## [41] DelayedArray_0.7.21         matrixStats_0.54.0         
## [43] Biobase_2.41.2              Rsamtools_1.33.3           
## [45] GenomicRanges_1.33.11       GenomeInfoDb_1.17.1        
## [47] Biostrings_2.49.0           XVector_0.21.3             
## [49] IRanges_2.15.16             S4Vectors_0.19.19          
## [51] BiocParallel_1.15.8         BiocGenerics_0.27.1        
## [53] dada2_1.8.0                 Rcpp_0.12.18               
## 
## loaded via a namespace (and not attached):
##  [1] colorspace_1.3-2       rjson_0.2.20           hwriter_1.3.2         
##  [4] rprojroot_1.3-2        GlobalOptions_0.1.0    base64enc_0.1-3       
##  [7] lubridate_1.7.4        xml2_1.2.0             codetools_0.2-15      
## [10] splines_3.5.0          ade4_1.7-11            jsonlite_1.5          
## [13] broom_0.5.0            shiny_1.1.0            compiler_3.5.0        
## [16] httr_1.3.1             backports_1.1.2        assertthat_0.2.0      
## [19] Matrix_1.2-14          lazyeval_0.2.1         cli_1.0.0             
## [22] later_0.7.3            htmltools_0.3.6        tools_3.5.0           
## [25] igraph_1.2.2           gtable_0.2.0           glue_1.3.0            
## [28] GenomeInfoDbData_1.1.0 cellranger_1.1.0       multtest_2.37.0       
## [31] ape_5.1                nlme_3.1-137           iterators_1.0.10      
## [34] crosstalk_1.0.0        fastcluster_1.1.25     rvest_0.3.2           
## [37] mime_0.5               gtools_3.8.1           zlibbioc_1.27.0       
## [40] MASS_7.3-50            promises_1.0.1         hms_0.4.2             
## [43] biomformat_1.8.0       rhdf5_2.25.4           RColorBrewer_1.1-2    
## [46] yaml_2.2.0             latticeExtra_0.6-28    stringi_1.2.4         
## [49] highr_0.7              foreach_1.4.4          shape_1.4.4           
## [52] rlang_0.2.1            pkgconfig_2.0.1        evaluate_0.11         
## [55] Rhdf5lib_1.2.1         bindr_0.1.1            labeling_0.3          
## [58] htmlwidgets_1.2        tidyselect_0.2.4       magrittr_1.5          
## [61] R6_2.2.2               pillar_1.3.0           haven_1.1.2           
## [64] withr_2.1.2            mgcv_1.8-24            survival_2.42-6       
## [67] modelr_0.1.2           crayon_1.3.4           GetoptLong_0.1.7      
## [70] readxl_1.1.0           digest_0.6.15          xtable_1.8-2          
## [73] httpuv_1.4.5           RcppParallel_4.4.1     munsell_0.5.0         
## [76] viridisLite_0.3.0
devtools::session_info()
## Session info -------------------------------------------------------------
##  setting  value                       
##  version  R version 3.5.0 (2018-04-23)
##  system   x86_64, darwin15.6.0        
##  ui       X11                         
##  language (EN)                        
##  collate  en_US.UTF-8                 
##  tz       America/Panama              
##  date     2018-11-24
## Packages -----------------------------------------------------------------
##  package              * version   date      
##  ade4                   1.7-11    2018-04-05
##  ape                    5.1       2018-04-04
##  assertthat             0.2.0     2017-04-11
##  backports              1.1.2     2017-12-13
##  base                 * 3.5.0     2018-04-24
##  base64enc              0.1-3     2015-07-28
##  bindr                  0.1.1     2018-03-13
##  bindrcpp             * 0.2.2     2018-03-29
##  Biobase              * 2.41.2    2018-07-18
##  BiocGenerics         * 0.27.1    2018-06-17
##  BiocParallel         * 1.15.8    2018-07-19
##  biomformat             1.8.0     2018-05-01
##  Biostrings           * 2.49.0    2018-05-01
##  bitops               * 1.0-6     2013-08-17
##  broom                  0.5.0     2018-07-17
##  cellranger             1.1.0     2016-07-27
##  circlize             * 0.4.4     2018-06-10
##  cli                    1.0.0     2017-11-05
##  cluster              * 2.0.7-1   2018-04-13
##  codetools              0.2-15    2016-10-05
##  colorspace             1.3-2     2016-12-14
##  compiler               3.5.0     2018-04-24
##  ComplexHeatmap       * 1.19.1    2018-06-19
##  crayon                 1.3.4     2017-09-16
##  crosstalk              1.0.0     2016-12-21
##  dada2                * 1.8.0     2018-07-31
##  data.table           * 1.11.4    2018-05-27
##  datasets             * 3.5.0     2018-04-24
##  DelayedArray         * 0.7.21    2018-07-23
##  devtools               1.13.6    2018-06-27
##  digest                 0.6.15    2018-01-28
##  dplyr                * 0.7.6     2018-06-29
##  DT                   * 0.4       2018-01-30
##  evaluate               0.11      2018-07-17
##  fastcluster            1.1.25    2018-06-07
##  forcats              * 0.3.0     2018-02-19
##  foreach                1.4.4     2017-12-12
##  formatR              * 1.5       2017-04-25
##  gdata                * 2.18.0    2017-06-06
##  GenomeInfoDb         * 1.17.1    2018-05-11
##  GenomeInfoDbData       1.1.0     2018-06-01
##  GenomicAlignments    * 1.17.3    2018-07-18
##  GenomicRanges        * 1.33.11   2018-07-20
##  GetoptLong             0.1.7     2018-06-10
##  ggplot2              * 3.0.0     2018-07-03
##  ggthemes             * 4.0.1     2018-08-24
##  GlobalOptions          0.1.0     2018-06-09
##  glue                   1.3.0     2018-07-17
##  graphics             * 3.5.0     2018-04-24
##  grDevices            * 3.5.0     2018-04-24
##  grid                 * 3.5.0     2018-04-24
##  gridExtra            * 2.3       2017-09-09
##  gtable                 0.2.0     2016-02-26
##  gtools                 3.8.1     2018-06-26
##  haven                  1.1.2     2018-06-27
##  heatmap3             * 1.1.1     2015-04-12
##  highr                  0.7       2018-06-09
##  hms                    0.4.2     2018-03-10
##  htmltools              0.3.6     2017-04-28
##  htmlwidgets            1.2       2018-04-19
##  httpuv                 1.4.5     2018-07-19
##  httr                   1.3.1     2017-08-20
##  hwriter                1.3.2     2014-09-10
##  igraph                 1.2.2     2018-07-27
##  IRanges              * 2.15.16   2018-07-18
##  iterators              1.0.10    2018-07-13
##  jsonlite               1.5       2017-06-01
##  kableExtra           * 0.9.0     2018-05-21
##  knitr                * 1.20      2018-02-20
##  labeling               0.3       2014-08-23
##  later                  0.7.3     2018-06-08
##  lattice              * 0.20-35   2017-03-25
##  latticeExtra           0.6-28    2016-02-09
##  lazyeval               0.2.1     2017-10-29
##  lubridate              1.7.4     2018-04-11
##  magrittr               1.5       2014-11-22
##  MASS                   7.3-50    2018-04-30
##  Matrix                 1.2-14    2018-04-13
##  matrixStats          * 0.54.0    2018-07-23
##  memoise                1.1.0     2017-04-21
##  methods              * 3.5.0     2018-04-24
##  mgcv                   1.8-24    2018-06-18
##  mime                   0.5       2016-07-07
##  modelr                 0.1.2     2018-05-11
##  multtest               2.37.0    2018-05-01
##  munsell                0.5.0     2018-06-12
##  nlme                   3.1-137   2018-04-07
##  pairwiseAdonis       * 0.0.1     2018-09-09
##  pander               * 0.6.2     2018-07-08
##  parallel             * 3.5.0     2018-04-24
##  permute              * 0.9-4     2016-09-09
##  phyloseq             * 1.25.2    2018-07-15
##  pillar                 1.3.0     2018-07-14
##  pkgconfig              2.0.1     2017-03-21
##  plotly               * 4.8.0     2018-07-20
##  plyr                 * 1.8.4     2016-06-08
##  promises               1.0.1     2018-04-13
##  purrr                * 0.2.5     2018-05-29
##  R6                     2.2.2     2017-06-17
##  RColorBrewer           1.1-2     2014-12-07
##  Rcpp                 * 0.12.18   2018-07-23
##  RcppParallel           4.4.1     2018-07-19
##  RCurl                * 1.95-4.11 2018-07-15
##  readr                * 1.1.1     2017-05-16
##  readxl                 1.1.0     2018-04-20
##  reshape2             * 1.4.3     2017-12-11
##  rhdf5                  2.25.4    2018-06-02
##  Rhdf5lib               1.2.1     2018-05-17
##  rjson                  0.2.20    2018-06-08
##  rlang                  0.2.1     2018-05-30
##  rmarkdown            * 1.10      2018-06-11
##  rprojroot              1.3-2     2018-01-03
##  Rsamtools            * 1.33.3    2018-07-20
##  rstudioapi           * 0.7       2017-09-07
##  rvest                  0.3.2     2016-06-17
##  S4Vectors            * 0.19.19   2018-07-18
##  scales               * 1.0.0     2018-08-09
##  shape                  1.4.4     2018-02-07
##  shiny                  1.1.0     2018-05-17
##  ShortRead            * 1.39.0    2018-06-13
##  splines                3.5.0     2018-04-24
##  stats                * 3.5.0     2018-04-24
##  stats4               * 3.5.0     2018-04-24
##  stringi                1.2.4     2018-07-20
##  stringr              * 1.3.1     2018-05-10
##  SummarizedExperiment * 1.11.6    2018-07-17
##  survival               2.42-6    2018-07-13
##  svgPanZoom           * 0.3.3     2018-08-23
##  tibble               * 1.4.2     2018-01-22
##  tidyr                * 0.8.1     2018-05-18
##  tidyselect             0.2.4     2018-02-26
##  tidyverse            * 1.2.1     2017-11-14
##  tools                  3.5.0     2018-04-24
##  utils                * 3.5.0     2018-04-24
##  vegan                * 2.5-2     2018-05-17
##  viridisLite            0.3.0     2018-02-01
##  withr                  2.1.2     2018-03-15
##  xml2                   1.2.0     2018-01-24
##  xtable                 1.8-2     2016-02-05
##  XVector              * 0.21.3    2018-06-23
##  yaml                   2.2.0     2018-07-25
##  zlibbioc               1.27.0    2018-05-01
##  source                                         
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  cran (@1.1.2)                                  
##  local                                          
##  cran (@0.1-3)                                  
##  cran (@0.1.1)                                  
##  cran (@0.2.2)                                  
##  Bioconductor                                   
##  Bioconductor                                   
##  Bioconductor                                   
##  Bioconductor                                   
##  Bioconductor                                   
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  local                                          
##  Bioconductor (R 3.5.0)                         
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  Github (benjjneb/dada2@630ef9a)                
##  CRAN (R 3.5.0)                                 
##  local                                          
##  Bioconductor                                   
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.1)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  cran (@1.1.25)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  Bioconductor                                   
##  Bioconductor                                   
##  Bioconductor                                   
##  Bioconductor                                   
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  local                                          
##  local                                          
##  local                                          
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  cran (@0.4.2)                                  
##  cran (@0.3.6)                                  
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  Bioconductor                                   
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  cran (@1.20)                                   
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  local                                          
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  Bioconductor                                   
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  Github (pmartinezarbizu/pairwiseAdonis@c8037b7)
##  CRAN (R 3.5.0)                                 
##  local                                          
##  CRAN (R 3.5.0)                                 
##  Bioconductor                                   
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  cran (@0.2.5)                                  
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  cran (@1.1.1)                                  
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  Bioconductor                                   
##  Bioconductor                                   
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  cran (@1.3-2)                                  
##  Bioconductor                                   
##  CRAN (R 3.5.0)                                 
##  cran (@0.3.2)                                  
##  Bioconductor                                   
##  cran (@1.0.0)                                  
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  Bioconductor                                   
##  local                                          
##  local                                          
##  local                                          
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  Bioconductor                                   
##  CRAN (R 3.5.0)                                 
##  Github (timelyportfolio/svgPanZoom@c0b2a46)    
##  CRAN (R 3.5.0)                                 
##  cran (@0.8.1)                                  
##  cran (@0.2.4)                                  
##  CRAN (R 3.5.0)                                 
##  local                                          
##  local                                          
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  CRAN (R 3.5.0)                                 
##  cran (@1.2.0)                                  
##  CRAN (R 3.5.0)                                 
##  Bioconductor                                   
##  CRAN (R 3.5.0)                                 
##  Bioconductor
end_time <- Sys.time()
end_time - start_time
## Time difference of 1.180127 mins

back to top

Author Affiliations


  1. Smithsonian Tropical Research Institute

  2. University of California, Santa Barbara

  3. Florida International University

  4. University of California, Santa Barbara

  5. Bigelow Laboratory for Ocean Sciences

LS0tCnRpdGxlOiBJbnRlc3RpbmFsIG1pY3JvYmVzIGFzIGFuIGF4aXMgb2YgbmljaGUgZGlmZmVyZW50aWF0aW9uIGFtb25nIGNvcmFsIHJlZWYgaGVyYml2b3JlcwpzdWJ0aXRsZTogU3VwcGxlbWVudGFyeSBGaWxlIDItLS1QaHlsb3NlcSB3b3JrZmxvdyAKYXV0aG9yOiBKYXJyb2QgSi4gU2NvdHReW1NtaXRoc29uaWFuIFRyb3BpY2FsIFJlc2VhcmNoIEluc3RpdHV0ZV0sIFRob21hcyBDLiBBZGFtXltVbml2ZXJzaXR5IG9mIENhbGlmb3JuaWEsIFNhbnRhIEJhcmJhcmFdLCBBbGFpbiBEdXJhbl5bRmxvcmlkYSBJbnRlcm5hdGlvbmFsIFVuaXZlcnNpdHldLCBEZXJvbiBFLiBCdXJrZXBpbGVeW1VuaXZlcnNpdHkgb2YgQ2FsaWZvcm5pYSwgU2FudGEgQmFyYmFyYV0sICYgRG91Z2xhcyBCLiBSYXNoZXJeW0JpZ2Vsb3cgTGFib3JhdG9yeSBmb3IgT2NlYW4gU2NpZW5jZXNdCmFic3RyYWN0OiAiV29ya2Zsb3cgdG8gZ2VuZXJhdGUgcGh5bG9zZXEgb2JqZWN0cywgYW5hbHl6ZSBkYXRhLCBhbmQgY3JlYXRlIGZpZ3VyZXMgZm9yIHRoZSBmaXNoIG1pY3JvYmlvbWUgcGFwZXIsIHByb2Nlc3NlZCB1c2luZyB0aGUgcHJldmlvdXMgREFEQTIgd29ya2Zsb3cuIEhlcmUgd2UgcmVwb3J0IGJvdGggbWV0aG9kcyBhbmQgcmVzdWx0cyBpbiBwbGFpbiB0ZXh0IGFuZCBSIGNvZGUuIFRoZSBkb2N1bWVudCByZXByZXNlbnRzIGEgKipjb21wbGV0ZWx5IHJlcHJvZHVjaWJsZSB3b3JrZmxvdyoqIG9mIG91ciBzdHVkeS4iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IAogICAga2VlcF9tZDogZmFsc2UKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgdGhlbWU6IGx1bWVuCiAgICBoaWdobGlnaHQ6IGhhZGRvY2sKICAgIGRmX3ByaW50OiBwYWdlZAogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogIHBkZl9kb2N1bWVudDogZGVmYXVsdAojY29vbF90aGVtZXM6IGRlZmF1bHQsIGZsYXRseSwgY29zbW8sIGx1bWVuLCBwYXBlciwgc2FuZHN0b25lLCB5ZXRpCi0tLQogCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpyZW1vdmUobGlzdCA9IGxzKCkpCmxpYnJhcnkoZGFkYTIpOyBwYWNrYWdlVmVyc2lvbigiZGFkYTIiKQpsaWJyYXJ5KFNob3J0UmVhZCk7IHBhY2thZ2VWZXJzaW9uKCJTaG9ydFJlYWQiKQpsaWJyYXJ5KHBoeWxvc2VxKTsgcGFja2FnZVZlcnNpb24oInBoeWxvc2VxIikKbGlicmFyeShnZ3Bsb3QyKTsgcGFja2FnZVZlcnNpb24oImdncGxvdDIiKQpsaWJyYXJ5KCJwbHlyIik7IHBhY2thZ2VWZXJzaW9uKCJwbHlyIikKbGlicmFyeSh2ZWdhbikKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShyc3R1ZGlvYXBpKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShEVCkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkocGFuZGVyKQpsaWJyYXJ5KGZvcm1hdFIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShncmlkKQpsaWJyYXJ5KGdyRGV2aWNlcykKbGlicmFyeShzdmdQYW5ab29tKSAKbGlicmFyeShSQ3VybCkKbGlicmFyeShwbG90bHkpCmxpYnJhcnkocGFpcndpc2VBZG9uaXMpCnNlc3Npb25JbmZvKCkKc2V0LnNlZWQoMDE5OSkKYGBgCgpgYGB7ciBzZXRfd2QsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gZ2V0d2QoKSkKIyBUaGlzIHdpbGwgc2V0d2QgdG8gd2hlcmV2ZXIgdGhlIC5SbWQgZmlsZSBpcyBvcGVuZWQuCnB0bSA8LSBwcm9jLnRpbWUoKQpzdGFydF90aW1lIDwtIFN5cy50aW1lKCkKIyBvcHRzX2NodW5rJHNldChjYWNoZT1UUlVFKQojZm9ybWF0Ujo6dGlkeV9hcHAoKSBydW4gdGhpcyBpbiBSIHRvIHRpZHkgY29kZS4gSG93IHRvIGRvIGl0IGhlcmU/CmBgYAoKKioqCgojIyBEYXRhIEF2YWlsYWJpbGl0eQoKQWxsIHlvdSBuZWVkIHRvIGRvIHRvIHJ1biB0aGlzIHdvcmtmbG93IGlzIHRvIGluc3RhbGwgdGhlIGFwcHJvcHJpYXRlIFIgcGFja2FnZXMgYW5kIGRvd25sb2FkIHRoZSBmb2xsb3dpbmcgZmlsZToKCiogaHR0cHM6Ly9kb2kub3JnLzEwLjYwODQvbTkuZmlnc2hhcmUuNzM1NzE3ODogRmluYWwgb3V0cHV0IGZpbGUgb2YgREFEQTIgd29ya2Zsb3cuIFRoaXMgZmlsZSBpcyBpbXBvcnRlZCBpbnRvIHBoeWxvc2VxIGZvciBjb21tdW5pdHkgYW5hbHlzaXMgYW5kIGlzIG5lZWRlZCB0byBydW4gdGhpcyBzY3JpcHQgKHNlZSBiZWxvdykuICAKCldlIGFsc28gbWFkZSBzZXZlcmFsIGFkZGl0aW9uYWwgZGF0YSBwcm9kdWN0cyBhbmQgcHJvY2Vzc2luZyBzY3JpcHRzIGF2YWlsYWJsZS4gCiogaHR0cHM6Ly9kb2kub3JnLzEwLjYwODQvbTkuZmlnc2hhcmUuNzM1NzE3ODogRE9JIGZvciB0aGlzIHdvcmtmbG93LgoqIGh0dHBzOi8vZG9pLm9yZy8xMC42MDg0L205LmZpZ3NoYXJlLjY4NzU1MjI6IFJhdyBkYXRhIGZvciBlYWNoIHNhbXBsZSAoYmVmb3JlIHJlbW92aW5nIHByaW1lcnMpLgoqIFRyaW1tZWQgZGF0YSAocHJpbWVycyByZW1vdmVkKSBhcmUgZGVwb3NpdGVkIGF0IHRoZSBFdXJvcGVhbiBOdWNsZW90aWRlIEFyY2hpdmUgdW5kZXIgdGhlIHN0dWR5IGFjY2Vzc2lvbiBudW1iZXIgW1BSSkVCMjgzOTcgKEVSUDExMDU5NCldKGh0dHBzOi8vd3d3LmViaS5hYy51ay9lbmEvZGF0YS92aWV3L1BSSkVCMjgzOTcpe3RhcmdldD0iX2JsYW5rIn0uCiogaHR0cHM6Ly9kb2kub3JnLzEwLjYwODQvbTkuZmlnc2hhcmUuNjk5NzI1MzogRE9JIGZvciB0aGUgREFEQTIgcHJvY2Vzc2luZyB3b3JrZmxvdy4KCioqKgoKPGEgaWQ9ImJhY2sgdG8gdG9wIj48L2E+ICAKCiMjIFByZWFtYmxlCgpUaGlzIGRvY3VtZW50IGlzICAqKmludGVyYWN0aXZlKiouIFlvdSBjYW4gKipzb3J0IGFuZCBzY3JvbGwqKiB0aHJvdWdoIG1vc3Qgb2YgdGhlIHRhYmxlcyBhbmQgdGhlICoqcGh5bG9nZW5ldGljIHRyZWUgaXMgem9vbWFibGUqKi4gSW4gdGhlIHVwcGVyIHJpZ2h0IGhhbmQgY29ybmVyIG9mIHRoZSBmcm9udCBwYWdlIGlzIGEgYENvZGVgIGJ1dHRvbi4gVXNlIHRoaXMgdG8gc2hvdyBvciBoaWRlIGFsbCB0aGUgY29kZSBpbiB0aGUgZG9jdW1lbnQgKGRlZmF1bHQgaXMgaGlkZSkgYXMgd2VsbCBhcyBkb3dubG9hZCB0aGUgYC5SbWRgIGZpbGUgd2hpY2ggeW91IGNhbiB1c2UgdG8gZXh0cmFjdCB0aGUgY29kZS4gTGV0J3MgcHJvY2VlZC4KCgo+IEltcG9ydGFudCBEZWZpbml0aW9ucyAmIEFiYnJldmlhdGlvbnMgIAoKKiBbQW1wbGljb24gU2VxdWVuY2UgVmFyaWFudF0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9pc21lajIwMTcxMTkpe3RhcmdldD0iX2JsYW5rIn0gKCoqQVNWKiopOiBFeGFjdCBzZXF1ZW5jZSB2YXJpYW50LS0tYW5hbG9nb3VzIHRvIGFuIE9UVS0tLWJ1dCB3aXRoIHNpbmdsZSBudWNsZW90aWRlIHJlc29sdXRpb24uICAKKiBEaWZmZXJlbnRpYWxseSBhYnVuZGFudCAoKipEQSoqKSBmZWF0dXJlOiBUYXhhLCBBU1YsIGV0Yy4gdGhhdCBpcyBkaXNwcm9wb3J0aW9uYXRlbHkgYWJ1bmRhbnQgaW4gYSBncm91cCBvZiBzYW1wbGVzIGFuZCBzdGF0aXN0aWNhbGx5IGRpZmZlcmVudCB0aGFuIG90aGVyIGdyb3Vwcy4gCgpPdXIgZ29hbHMgb2YgdGhpcyBzdHVkeSB3ZXJlIHRvOgoKMS4gQXNzZXNzIHRoZSB0YXhvbm9taWMgY29tcG9zaXRpb24gb2YgaW50ZXN0aW5hbCBjb21tdW5pdGllcyBmcm9tIGhlcmJpdm9yb3VzIHJlZWYgZmlzaC4gIAoyLiBEZXRlcm1pbmUgdGhlIGRpdmVyc2l0eSBvZiB0aGVzZSBjb21tdW5pdGllcyBhbmQgdGhlaXIgc2ltaWxhcml0eS9kaXNzaW1pbGFyaXR5LiAgCjMuIElkZW50aWZ5IGRpZmZlcmVudGlhbGx5IGFidW5kYW50IEFTVnMgYWNyb3NzIHRoZSBob3N0IHNwZWNpZXMuICAKNC4gUHJlZGljdCB0aGUgc3BlY2lmaWNpdHkgb2YgZGlmZmVyZW50aWFsbHkgYWJ1bmRhbnQgQVNWcy4gIAoKKioqCiMjIFdvcmtmbG93IG92ZXJ2aWV3CgojIyMjW1BhcnQgSTogRmllbGQgT2JzZXJ2YXRpb25zXSgjUGFydCBJOiBGaWVsZCBPYnNlcnZhdGlvbnMpCgpCZWZvcmUgd2UgcHJvY2VlZCB3aXRoIG1pY3JvYmlhbCBjb21tdW5pdHkgYW5hbHlzaXMsIHdlIHdpbGwgcnVuIHNvbWUgYW5hbHlzZXMgb24gZmllbGQtYmFzZWQgYmVoYXZpb3JhbCBhc3NheXMgb2YgdGhlIGRpZmZlcmVudCBoZXJiaXZvcm91cyByZWVmIGZpc2ggc3BlY2llcy4KCgojIyMjW1BhcnQgSUk6IERhdGEgUHJlcGFyYXRpb25dKCNQYXJ0IElJOiBEYXRhIFByZXBhcmF0aW9uKQoKSW4gdGhpcyBmaXJzdCBwYXJ0IHdlIGdvIHRocm91Z2ggdGhlIHN0ZXBzIG9mIGRlZmluaW5nIHNhbXBsZSBncm91cHMsIGNyZWF0aW5nIHBoeWxvc2VxIG9iamVjdHMsIHJlbW92aW5nIHVud2FudGVkIHNhbXBsZXMsIGFuZCByZW1vdmluZyBjb250YW1pbmFudCBBU1ZzLiBWYXJpb3VzIHBhcnRzIG9mIHRoaXMgc2VjdGlvbiBjYW4gZWFzaWx5IGJlIG1vZGlmaWVkIHRvIHBlcmZvcm0gZGlmZmVyZW50IGFuYWx5c2VzLiBGb3IgZXhhbXBsZSwgaWYgeW91IHdlcmUgb25seSBpbnRlcmVzdGVkIGluIGEgc3BlY2lmaWMgdGF4YSBvciBncm91cCBvZiBzYW1wbGVzLCB5b3UgY291bGQgY2hhbmdlIHRoZSBjb2RlIGhlcmUgdG8gY3JlYXRlIG5ldyBwaHlsb3NlcSBvYmplY3RzLgoKIyMjI1tQYXJ0IElJSTogQ29tbXVuaXR5IENvbXBvc2l0aW9uICYgRGl2ZXJzaXR5XSgjUGFydCBJSUk6IENvbW11bml0eSBDb21wb3NpdGlvbiAmIERpdmVyc2l0eSkgIAoKSW4gdGhlIHNlY29uZCBwYXJ0LCB3ZSBhc3Nlc3MgdGF4b25vbWljIGNvbXBvc2l0aW9uIGFzIHdlbGwgYXMgYWxwaGEgYW5kIGJldGEgZGl2ZXJzaXR5LiBQaHlsb3NlcSBvZmZlcnMgbWFueSBvcHRpb25zIGZvciBhc3Nlc3NpbmcgZGl2ZXJzaXR5LCBpbmNsdWRpbmcgc2V2ZXJhbCBhbHBoYSBkaXZlcnNpdHkgbWV0cmljcywgYWRkaXRpb25hbCBvcmRpbmF0aW9uICBhbmQgZGlzdGFuY2UgbWV0aG9kcywgYW5kIHNvIG9uLiBZb3UgY2FuIHBsYXkgYXJvdW5kIHdpdGggdGhlc2Ugc2V0dGluZ3MgdG8gaG93IGl0IGFmZmVjdHMgdGhlIHJlc3VsdHMuCgojIyMjW1BhcnQgSVY6IERpZmZlcmVudGlhbGx5IEFidW5kYW50IEFTVnNdKCNQYXJ0IElWOiBEaWZmZXJlbnRpYWxseSBBYnVuZGFudCBBU1ZzKSAKCldlIHdhbnRlZCB0byB1bmRlcnN0YW5kIGhvdyBBU1ZzIHBhcnRpdGlvbmVkIGFjcm9zcyBob3N0IHNwZWNpZXMuIFdlIGFsc28gd2FudGVkIHRvIGFzc2VzcyB0aGUgc3BlY2lmaWNpdHkgb2YgZWFjaCBBU1YgdG8gZGV0ZXJtaW5lIGhhYml0YXQgcHJlZmVyZW5jZS4gVG8gb3VyIGtub3dsZWRnZSB0aGVyZSBpcyBubyBxdWFudGl0YXRpdmUgd2F5IHRvIGRvIHRoaXMuIFRoZSBvbmx5IGF0dGVtcHQgd2UgYXJlIGF3YXJlIG9mIHdhcyBbTWV0YU1ldGFEQl0oaHR0cDovL21tZGIuYW9yaS51LXRva3lvLmFjLmpwLyl7dGFyZ2V0PSJfYmxhbmsifSBidXQgaXQgaXMgYmFzZWQgb24gYSA0NTQgZGF0YWJhc2UgYW5kIG5vIGxvbmdlciBzZWVtcyB0byBiZSBpbiBhY3RpdmUgZGV2ZWxvcG1lbnQuIFNvIHdlIHVzZWQgYW4gYXBwcm9hY2ggYmFzZWQgb24gdGhlIHdvcmsgb2YgW1N1bGxhbSAqZXQuIGFsLipdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTExL2ouMTM2NS0yOTRYLjIwMTIuMDU1NTIueCl7dGFyZ2V0PSJfYmxhbmsifSwgZmlyc3QgaWRlbnRpZnlpbmcgZGlmZmVyZW50aWFsbHkgYWJ1bmRhbnQgQVNWcywgdGhlbiBzZWFyY2hpbmcgZm9yIGNsb3Nlc3QgZGF0YWJhc2UgaGl0cywgYW5kIGZpbmFsbHkgdXNpbmcgcGh5bG9nZW5ldGljIGFuYWx5c2lzIGFuZCB0b3AgaGl0IG1ldGFkYXRhIChpc29sYXRpb24gc291cmNlLCBuYXR1cmFsIGhvc3QpIHRvIGluZmVyIGhhYml0YXQgcHJlZmVyZW5jZS4KCiMjIyNbUGFydCBWOiBTeW50aGVzaXNdKCNQYXJ0IFY6IFN5bnRoZXNpcykKCkluIHRoaXMgc2VjdGlvbiB3ZSBwdWxsIHRvZ2V0aGVyIHRoZSByZXN1bHRzIGZyb20gKipQYXJ0IElJSSoqIGFuZCB0cnkgdG8gbWFrZSBzZW5zZSBvZiB0aGUgbWljcm9iaW9tZXMgZnJvbSB0aGVzZSBoZXJiaXZvcm91cyByZWVmIGZpc2guIEhvdyBhcmUgQVNWcyBwYXJ0aXRpb25pbmcgYWNyb3NzIGhvc3Q/IEhvdyBzaW1pbGFyIGFyZSB0aGVzZSBBU1ZzIHRvIHNlcXVlbmNlcyBmcm9tIG90aGVyIHN0dWRpZXM/IFdoYXQgY2FuIHRoZXNlIHBhdHRlcm5zIHRlbGwgdXMgYWJvdXQgaG9zdCBzcGVjaWZpY2l0eT8KCj4gQWxsIHRhYmxlcyBhbmQgZmlndXJlcyBwcmVzZW50ZWQgYmVsb3cgYXJlIG5hbWVkIGFzIHRoZXkgYXBwZWFyZWQgaW4gdGhlIG9yaWdpbmFsIHB1YmxpY2F0aW9uLiBXZSBhbHNvIGluY2x1ZGUgbWFueSBhZGRpdGlvbmFsIGRhdGEgcHJvZHVjdGVzIHRoYXQgd2VyZSBub3QgcGFydCBvZiB0aGUgb3JpZ2luYWwgcHVibGljYXRpb24uICAKCioqKgoKIyMjIENvbG9yICYgZ3JhcGhpY3MKClRocm91Z2hvdXQgdGhpcyB3b3JrZmxvdyB3ZSBhcmUgZ29pbmcgdG8gcmVseSBvbiBjb2xvciB0byBoZWxwIHVzIHRlbGwgb3VyIHN0b3J5LiBXZSB3aWxsIHVzZSBjb2xvciB0byBkZWxpbmVhdGUgaG9zdCBmaXNoIHNwZWNpZXMsIGJ1dCBtb3JlIGltcG9ydGFudGx5LCB0byBkZWxpbmVhdGUgbWljcm9iaWFsIHRheGEuIE1pY3JvYmlhbCBkaXZlcnNpdHkgaXMgcHJldHR5IHZhc3QgYW5kIGl0IGNhbiBiZSBkaWZmaWN1bHQgdG8gZGlzcGxheSBhbGwgb2YgdGhpcyBkaXZlcnNpdHkgaW4gYSBzaW5nbGUsIHN0YXRpYyBmaWd1cmUuIEFkZGl0aW9uYWxseSwgbWFueSBvZiB1cyBoYXZlIGEgZGVjcmVhc2VkIGFiaWxpdHkgdG8gc2VlIGNvbG9yIG9yIGRpZmZlcmVuY2VzIGluIGNvbG9yLiBTbyBpdCBpcyBub3Qgb25seSBpbXBvcnRhbnQgdG8gdXNlIHJlbGF0aXZlbHkgZmV3IGNvbG9ycyBidXQgYWxzbyBhIGNvbG9yIGJsaW5kIGZyaWVuZGx5IHBhbGV0dGUuIEZvciBvdXIgZmlndXJlcywgd2UgZ2VuZXJhdGVkIGEgcGFsZXR0ZXMgYmFzZWQgb24gQmFuZyBXb25nJ3Mgc2NoZW1lIGRlc2NyaWJlZCBpbiB0aGlzIFtwYXBlcl0oaHR0cDovL2R4LmRvaS5vcmcvMTAuMTAzOC9ubWV0aC4xNjE4KXt0YXJnZXQ9Il9ibGFuayJ9LiBXb25nJ3MgY29sb3Igc2NoZW1lIHVzZXMgY29udHJhc3RpbmcgY29sb3JzIHRoYXQgY2FuIGJlIGRpc3Rpbmd1aXNoZWQgYnkgZm9sa3Mgd2l0aCBjb2xvciB2aXNpb24gZGVmaWNpZW5jeS0tLXJvdWdobHkgOCUgb2YgcGVvcGxlIChtb3N0bHkgbWFsZXMpIGFyZSBjb2xvciBibGluZC4gRG8gd2FudCBLZWFudSBSZWV2ZXMgdG8gdW5kZXJzdGFuZCB5b3VyIGZpZ3VyZXMgb3Igbm90PyAKClRoaXMgc2NoZW1lIGlzIGNvbnNlcnZhdGl2ZS0tLXRoZXJlIGFyZSBvbmx5IDcgY29sb3JzLiBXZSBhZGRlZCBibGFjayBhbmQgZ3JleSB0byBnaXZlIHVzIGEgbGl0dGxlIHdpZ2dsZSByb29tLiBPdGhlcnMgaGF2ZSBkZXZlbG9wZWQgWzEyIGFuZCAxNSBjb2xvciBwYWxldHRlIHNjaGVtZXNdKGh0dHA6Ly9ta3dlYi5iY2dzYy5jYS9jb2xvcmJsaW5kLyl7dGFyZ2V0PSJfYmxhbmsifSwgYnV0IGJlIGNhcmVmdWwtLS1maWd1cmVzIHdpdGggdG9vIG1hbnkgY29sb3JzIGNhbiBpbmhpYml0IG91ciBhYmlsaXR5IHRvIGRpc2Nlcm4gcGF0dGVybnMuIE91ciBjb25zZXJ2YXRpdmUgcGFsZXR0ZSBmb3JjZXMgdXMgdG8gY2hvb3NlIGNhcmVmdWxseSB3aGVuIGRlY2lkaW5nIHdoaWNoIHRheGEgdG8gdGFyZ2V0IG9yIGhvdyBtYW55IGdyb3VwcyB0byBkaXNwbGF5LiBIZXJlIHdlIHdpbGwgY3JlYXRlIHR3byBwYWxldHRlcy0tLW9uZSBmb3IgbWljcm9iaWFsIHRheGEgd2l0aCBhbGwgdGhlIGNvbG9ycyBhbmQgYW5vdGhlciBmb3IgdGhlIGZpdmUgaG9zdCBmaXNoIHNwZWNpZXMuIFRoZSBsYXR0ZXIgaXMganVzdCBhIHN1YnNldCBvZiB0aGUgZnVsbCBwYWxldHRlLiBIZXJlIGlzIHRoZSBjb2RlOgoKYGBge3IgZGVmaW5lX2NvbG9yX2JsaW5kX3NjaGVtZX0KI0Z1bGwgcGFsZXR0ZQpmcmllbmRfcGFsIDwtIGMoIiMwMDlFNzMiLCAiI0Q1NUUwMCIsICIjRjBFNDQyIiwgIiNDQzc5QTciLCAiIzU2QjRFOSIsIAogICAgIiNFNjlGMDAiLCAiIzAwNzJCMiIsICIjN0Y3RjdGIiwgIiMwMDAwMDAiLCAiI0ZGRkZGRiIpCiNGaXNoIHBhbGV0dGUKc2FtcF9wYWwgPC0gYygiI0NDNzlBNyIsICIjMDA3MkIyIiwgIiMwMDlFNzMiLCAiIzU2QjRFOSIsICIjRTY5RjAwIikKYGBgCgoKKioqCjxhIGlkPSJQYXJ0IEk6IEZpZWxkIE9ic2VydmF0aW9ucyI+PC9hPiAgCgojIyBQYXJ0IEk6IEZpZWxkIE9ic2VydmF0aW9ucwoKVG8gY2hhcmFjdGVyaXplIHRoZSBmb3JhZ2luZyBlY29sb2d5IG9mIHRoZSBkaWZmZXJlbnQgc3BlY2llcyBvZiBoZXJiaXZvcm91cyBmaXNoZXMsIHdlIGNoYXJhY3Rlcml6ZWQgaW4gZGV0YWlsIGluZGl2aWR1YWwgYml0ZXMgYnkgZWFjaCBzcGVjaWVzIGF0IHRocmVlIHNpdGVzIGluIHRoZSBGbG9yaWRhIEtleXMgKENvbmNoLCBGcmVuY2gsIE1vbGFzc2VzIHJlZWZzKSBkdXJpbmcgdGhlIEJvcmVhbCBzdW1tZXJzIG9mIDIwMTQgYW5kIDIwMTYuIEF0IGVhY2ggc2l0ZSwgd2UgaGFwaGF6YXJkbHkgc2VsZWN0ZWQgZm9jYWwgZmlzaCBvdmVyIGEgd2lkZSByYW5nZSBvZiBzaXplcyBhbmQgdGhlbiByYW5kb21seSBzZWxlY3RlZCBhIHNpbmdsZSBiaXRlIGJ5IGVhY2ggaW5kaXZpZHVhbCB0byBkZXNjcmliZSAoc2VlIFN1cHBsZW1lbnRhcnkgVGFibGUgMiBmb3Igc2FtcGxlIHNpemVzKS4gRm9yIGVhY2ggYml0ZSwgd2UgaWRlbnRpZmllZCB0aGUgZm9vZCBpdGVtKHMpIHRhcmdldGVkIGFzIHdlbGwgYXMgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBzdWJzdHJhdGUgKGUuZy4sIGhhcmQgYm90dG9tIHZzLiBvdGhlciBjb21tb24gc3Vic3RyYXRlcyBzdWNoIGFzIHNwb25nZXMsIGdvcmdvbmlhbnMsIGV0Yy4pIGF0IHRoZSBwcmVjaXNlIGxvY2F0aW9uIG9mIHRoZSBiaXRlLiBGb3IgaGFyZCBzdWJzdHJhdGVzLCB3ZSByZWNvcmRlZCB3aGV0aGVyIGEgYml0ZSB3YXMgb24gYSBjb252ZXgsIGNvbmNhdmUsIG9yIGZsYXQgc3VyZmFjZSwgYW5kIHdoZXRoZXIgdGhhdCBzdXJmYWNlIHdhcyBvcmllbnRlZCBob3Jpem9udGFsbHkgKDwgNDUgZGVncmVlcykgb3IgdmVydGljYWxseSAoPiA0NSBkZWdyZWVzKS4gSW4gYWRkaXRpb24sIHdlIGZyYW1lZCBlYWNoIGJpdGUgd2l0aGluIGEgNSB4IDUgY20gbWljcm8tcXVhZHJhdCBhbmQgbWVhc3VyZWQgdGhlIGRlcHRoIG9mIHRoZSBzZWRpbWVudCBhbmQgaGVpZ2h0IG9mIHRoZSBhbGdhZSBhdCBzZXZlcmFsIHBvaW50cyB0byBkZXRlcm1pbmUgdGhlIGF2ZXJhZ2Ugc2VkaW1lbnQgZGVwdGggYW5kIGFsZ2FsIGhlaWdodCB3aXRoaW4gdGhlIHZpY2luaXR5IG9mIHRoZSBiaXRlMzYuIFdlIHRoZW4gbWFudWFsbHkgcmVtb3ZlZCBzZWRpbWVudHMgYW5kIGRldGVybWluZWQgd2hldGhlciB0aGUgZmlzaCBsZWZ0IGEgZGlzdGluY3QgZ3JhemluZyBzY2FyIChpLmUuLCB3aGVyZSBjYWxjaXVtIGNhcmJvbmF0ZSBoYWQgYmVlbiByZW1vdmVkIGZyb20gdGhlIHJlZWYgZnJhbWV3b3JrIGluIGFkZGl0aW9uIHRvIGVwaWxpdGhpYyBhbGdhZSkuIAoKVG8gdmlzdWFsaXplIHRoZSBtdWx0aXZhcmlhdGUgcGF0dGVybnMgb2YgaGVyYml2b3J5IHdlIHVzZWQgbm9uLW1ldHJpYyBtdWx0aWRpbWVuc2lvbmFsIHNjYWxpbmcgKE5NRFMpIGFzIGltcGxlbWVudGVkIHZpYSB0aGUgbWV0YU1EUyBmdW5jdGlvbiBpbiB0aGUgdmVnYW4gcGFja2FnZS4gRm9yIGVhY2ggc3BlY2llcyBvZiBoZXJiaXZvcm91cyBmaXNoIGF0IGVhY2ggc2l0ZSB3ZSBjYWxjdWxhdGVkIHRoZSBwcm9wb3J0aW9uIG9mIGJpdGVzIGZvY3VzZWQgb24gZWFjaCBwcmV5IGl0ZW0gKOKAmHByZXkgdmFyaWFibGVz4oCZKSBhcyB3ZWxsIGFzIHRoZSBwcm9wb3J0aW9uIG9mIGJpdGVzIHRhcmdldGluZyBzdWJzdHJhdGVzIHdpdGggZGlmZmVyZW50IGNoYXJhY3RlcmlzdGljcyAoZS5nLiwgY29udmV4IHZzLiBjb25jYXZlIHZzLiBmbGF0KS4gV2UgYWxzbyBjYWxjdWxhdGVkIHRoZSBwcm9wb3J0aW9uIG9mIGJpdGVzIHJlc3VsdGluZyBpbiBhIGdyYXppbmcgc2Nhci4gRm9yIGJpdGVzIG9uIHR1cmYgYXNzZW1ibGFnZXMsIHdlIGNhbGN1bGF0ZWQgdGhlIG1lYW4gdHVyZiBoZWlnaHQgYW5kIHNlZGltZW50IGRlcHRoIGRpcmVjdGx5IGFkamFjZW50IHRvIGVhY2ggYml0ZS4gUHJpb3IgdG8gYW5hbHlzaXMsIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMgKGUuZy4sIHNlZGltZW50IGRlcHRoIGFuZCB0dXJmIGhlaWdodCkgd2VyZSByZXNjYWxlZCB0byB0aGUgcmFuZ2Ugb2YgMCB0byAxLiBJbiBhZGRpdGlvbiwgcXVhbnRpdGF0aXZlIGFuZCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgd2VyZSByZXNjYWxlZCBzdWNoIHRoYXQgdGhleSB3b3VsZCBoYXZlIHNpbWlsYXIgaW5mbHVlbmNlIHRvIHRoZSDigJhwcmV5IHZhcmlhYmxlc+KAmSBieSBkaXZpZGluZyBlYWNoIHZhcmlhYmxlIGJ5IHRoZSBudW1iZXIgb2YgcHJleSBjYXRlZ29yaWVzLiBSZXNjYWxlZCBkYXRhIHdlcmUgdGhlbiBhbmFseXNlZCB2aWEgTk1EUyB1c2luZyBhIEJyYXktQ3VydGlzIGRpc3NpbWlsYXJpdHkgbWF0cml4LiAKCgojIyMjPGZvbnQgY29sb3I9InJlZCI+U3VwcGxlbWVudGFyeSBUYWJsZSAxPC9mb250PgoKRmlyc3QsIHdlIHJlYWQgaW4gYW5kIGRpc3BsYXkgdGhlIHRhYmxlIGRlc2NyaWJpbmcgdGhlIGZvcmFnaW5nIGVjb2xvZ3kgb2YgdGhlc2UgZmlzaC4KCmBgYHtyIG1lYW5fYml0ZV9hbmFseXNpc30KbGlicmFyeShnZ3RoZW1lcykKYWxsX3RyYWl0cyA8LSByZWFkLmNzdigiTUFOVUFMX0lOUFVUL01lYW5fYml0ZV9jaGFyYWN0ZXJpc3RpY3MudHh0IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIlx0IikKaWRzIDwtIGFsbF90cmFpdHNbLDE6Ml0KCndyaXRlLnRhYmxlKGFsbF90cmFpdHMsICJSX09VVFBVVC9TdXBwbGVtZW50YXJ5X1RhYmxlXzEudHh0Iiwgc2VwID0gIlx0IiwgCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKZGF0YXRhYmxlKGFsbF90cmFpdHMsIHJvd25hbWVzID0gRkFMU0UsIHdpZHRoID0gIjEwMCUiLCBjb2xuYW1lcyA9IGMoCiAgICAgICAgIkhvc3Qgc3BlY2llcyIsICJTaXRlIiwgIlNlZGltZW50IGRlcHRoIChtbSkiLCAiVHVyZiBoZWlnaHQgKG1tKSIsIAoiUHJvcCBtYXJrIG9uIHN1YnN0cmF0ZSIsICJWZXJ0aWNhbF9zdWJzdHJhdGUiLCAiUHJvcCBjb25jYXZlIiwgCiJQcm9wIGNvbnZleCIsICJBcnRpY3VsYXRlZCBjb3JhbGxpbmUiLCAiQ0NBIiwgIkRpY3R5b3RhIiwgImVwaXBoeXRlcyIsIAoiR29yZ29uaWFuIiwgIkhhbGltZWRhIiwgIkxhdXJlbmNpYSIsICJzcG9uZ2UiLCAiU3R5cG9wb2RpdW0iLCAidHVyZiIpLCAKICAgIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbigKICAgICAgc3R5bGUgPSAiY2FwdGlvbi1zaWRlOiBib3R0b207IHRleHQtYWxpZ246IGxlZnQ7IiwgCiAgICAgICAgICAgICJTdXBwbGVtZW50YXJ5IFRhYmxlIDE6ICIsIAogICAgaHRtbHRvb2xzOjplbSgiSU5TRVJUIERFU0NSSVBUSU9OLiIpKSwgCiAgICBleHRlbnNpb25zID0gIkJ1dHRvbnMiLCAKICAgIG9wdGlvbnMgPSBsaXN0KGNvbHVtbkRlZnMgPSBsaXN0KGxpc3QoY2xhc3NOYW1lID0gImR0LWxlZnQiLCB0YXJnZXRzID0gMCkpLCAKICAgICAgICBkb20gPSAiQmxydGlwIiwgcGFnZUxlbmd0aCA9IDUsIGxlbmd0aE1lbnUgPSBjKDUsIDEwLCAxNSksIAogICAgICAgIGJ1dHRvbnMgPSBjKCJjc3YiLCAiY29weSIpLCBzY3JvbGxYID0gVFJVRSwgc2Nyb2xsQ29sbGFwc2UgPSBUUlVFKSkKCmBgYAoqKk5vdGUqKiBUYWJsZSBzY3JvbGxzIGhvcml6b250YWxseS4KCk5leHQgd2Ugc3RhbmRhcmRpemUgdGhlIHZhcmlhYmxlcyBzbyB0aGF0IHRoZXkgaGF2ZSBzaW1pbGFyIHdlaWdodHMuIEZpcnN0IHdlIHJlc2NhbGUgcXVhbnRpdGF0aXZlIHRyYWl0cyB0byBiZSBpbiB0aGUgcmFuZ2UgMCB0byAxIGFuZCBkaXZpZGUgYnkgdGhlIG51bWJlciBvZiBkaWV0IGNhdGVnb3JpZXMgdG8gaGF2ZSBzaW1pbGFyIGluZmx1ZW5jZSBhcyB0aGUgZGlldCB2YXJpYWJsZXMuIE5leHQgd2UgcmVzY2FsZSBhbGwgJ25vbiBkaWV0JyB0cmFpdHMgdG8gaGF2ZSBzaW1pbGFyIGluZmx1ZW5jZSB0byB0aGUgZGlldCB0cmFpdHMgYnkgZGl2aWRpbmcgYnkgdGhlIG51bWJlciBvZiBkaWV0IGNhdGVnb3JpZXMgZGl2aWRlZCBieSB0aGUgbnVtYmVyIG9mIGNhdGVnb3JpZXMgZm9yIGVhY2ggc3Vic3RyYXRlIGNoYXJhY3RlcmlzdGljLiBMYXN0bHksIHdlIGNvbWJpbmUgdGhlc2UgZGF0YSBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUgZm9yIE5NRFMgYW5hbHlzaXMuCgpgYGB7ciBzdGFuZGFyZGl6ZV9kaWV0X3ZhcmlhYmxlc30KI0ZpcnN0CnF1YW50X3RyYWl0c19zdGQgPC0gZGVjb3N0YW5kKGFsbF90cmFpdHNbLDM6NF0sICdyYW5nZScpLzEwCiNTZWNvbmQKTWVhbl9wcm9wX21hcmtfb25fc3Vic3RyYXRlX3N0ZCA8LSBhbGxfdHJhaXRzWyw1XS8oMTAvMikKcHJvcF92ZXJ0aWNhbF9zdGQgPC0gYWxsX3RyYWl0c1ssNl0vKDEwLzIpCnByb3BfY29uY2F2ZV9zdGQgPC0gYWxsX3RyYWl0c1ssN10vKDEwLzMpCnByb3BfY29udmV4X3N0ZCA8LSBhbGxfdHJhaXRzWyw4XS8oMTAvMykKCmFsbF90cmFpdHNfc3RkIDwtIGNiaW5kKHF1YW50X3RyYWl0c19zdGQsIAogICAgICAgICAgICAgICAgICAgICAgICBNZWFuX3Byb3BfbWFya19vbl9zdWJzdHJhdGVfc3RkLCAKICAgICAgICAgICAgICAgICAgICAgICAgcHJvcF92ZXJ0aWNhbF9zdGQsIHByb3BfY29uY2F2ZV9zdGQsIAogICAgICAgICAgICAgICAgICAgICAgICBwcm9wX2NvbnZleF9zdGQsIGFsbF90cmFpdHNbLDk6MThdKQpgYGAKCk5vdyB3ZSBjb25kdWN0IHRoZSBOTURTIGFuYWx5c2lzIG9mIHRoZSBzdGFuZGFyZGl6ZWQgZGlldCBkYXRhLiAKCmBgYHtyIGRpZXRfTk1EUywgcmVzdWx0cyA9ICdoaWRlJ30Kbm1kcyA8LSBtZXRhTURTKGFsbF90cmFpdHNfc3RkLCBkaXN0YW5jZSA9ICJicmF5IiwgayA9IDMsIHRyeW1heCA9IDQwKQpgYGAKClRoZW4gd2UgY2FuIGluc3BlY3QgdGhlIFNoZXBwYXJkIHBsb3QgIGFuZCBzYXZlIHRoZSByZXN1bHRzIGFzIGEgZGF0YWZyYW1lLiAKCmBgYHtyIGRpZXRfTk1EU19tb2RzfQoKc3RyZXNzcGxvdChubWRzKQoKI3Bsb3Qobm1kcywgdHlwZSA9ICJ0IikKCk5NRFMgPC0gZGF0YS5mcmFtZShOTURTMSA9IG5tZHMkcG9pbnRzWywxXSwgTk1EUzIgPSBubWRzJHBvaW50c1ssMl0sIE1EUzMgPSBubWRzJHBvaW50c1ssM10pCmBgYAoKRmluYWxseSwgd2UgIGdlbmVyYXRlIHRoZSBlbnZpcm9ubWVudGFsIHZlY3RvcnMgKGNvcnJlbGF0aW9ucyBvZiB2YXJpYWJsZXMgd2l0aCBvcmRpbmF0aW9uIGF4ZXMpLgoKYGBge3IgZ2V0X2Vudl92ZWN0b3JzfQpzZXQuc2VlZCgxKQp2ZWMuc3AgPC0gZW52Zml0KG5tZHMkcG9pbnRzLCBhbGxfdHJhaXRzWywzOjE4XSwgcGVybT0xMDAwLCBjaG9pY2VzID0gYygxLDIsMykpCnNwcC5zY3JzIDwtIGFzLmRhdGEuZnJhbWUoc2NvcmVzKHZlYy5zcCwgZGlzcGxheSA9ICJ2ZWN0b3JzIikpCnNwcC5zY3JzJHNwZWNpZXMgPC0gcm93bmFtZXMoc3BwLnNjcnMpCmNvbG5hbWVzKHNwcC5zY3JzKSA8LSBjKCJTX01EUzEiLCAiU19NRFMyIiwgIlNNRFMzIiwgInNwZWNpZXMiKQpgYGAKCk5vdyB3ZSBjYW4gIHBsb3QgdGhlIHJlc3VsdHMgYW5kIGdlbmVyYXRlICoqRmlndXJlIDEqKiBmcm9tIHRoZSBtYW51c2NyaXB0LiBGb3IgYWxsIGZpZ3VyZXMgZ2VuZXJhdGVkIGluIHRoaXMgd29ya2Zsb3csIHdlIGNvZGVkIGFzIG11Y2ggZm9ybWF0dGluZyBhcyB3ZSBjb3VsZCB3aXRoIFIgYW5kIHRoZW4gbWFkZSBtaW5vciBzdHlsaXN0aWMgY2hhbmdlcyBpbiBJbnNrc2NhcGUuIFRodXMsIHdoYXQgeW91IHNlZSBoZXJlIGFuZCB0aHJvdWdob3V0LCBhcmUgdGhlIHJhdyBmaWd1cmVzLiAKCmBgYHtyIG1lYW5fYml0ZV9pbml0aWFsX3Bsb3R9CiNQbG90IHJlc3VsdHMgdXNpbmcgZ2dwbG90MgojTWVyZ2Ugd2l0aCBkYXRhIHRoYXQgd2Ugd2FudCB0byB1c2UgaW4gcGxvdHRpbmcKTk1EUyA8LSBjYmluZChpZHMsIE5NRFMpCnN0dWZmIDwtIGdncGxvdChOTURTKSArCiAJZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBOTURTMSwgeSA9IE5NRFMyLCBzaGFwZSA9IFNpdGUsIGNvbG91ciA9IFNwZWNpZXMpLCBzaXplID00KSsKCWdlb21fc2VnbWVudChhZXMoeD0wLHk9MCx4ZW5kPVNfTURTMSwgeWVuZD1TX01EUzIpLCBkYXRhID0gc3BwLnNjcnMsCiAgICAgIAlhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC41LCAiY20iKSksY29sb3VyPSJibGFjayIsaW5oZXJpdC5hZXM9RkFMU0UpKwogCWdlb21fdGV4dChkYXRhPXNwcC5zY3JzLGFlcyh4PVNfTURTMSx5PVNfTURTMixsYWJlbD1zcGVjaWVzKSxzaXplPTMpKwoJbGFicyh0aXRsZSA9ICJicmF5IGRpc3RhbmNlIG9uIGFsbCB0cmFpdHMgKHN0YW5kYXJkaXplZCkiLCBzdWJ0aXRsZSA9ICJTdHJlc3MgPSAwLjA0IikKCiMjIyMjTm93IHN1YnNldCBqdXN0IHRoZSBzcHAuIHNjcnMgd2l0aCB0aGUgaGlnaGVzdCBjb3JyZWxhdGlvbnMKc3BwLnNjcnNfc3ViIDwtIHNwcC5zY3JzWy1jKDUsMTEsMTIpLF0KI0NhdGVnb3JpemUgc3BlY2llcyBzY29yZXMgaW50byBkaWZmZXJlbnQgdmFyaWFibGUgdHlwZXMgZm9yIHBsb3R0aW5nCnZhcmlhYmxlX3R5cGUgPC0gYXMuZmFjdG9yKGMocmVwKCJzdWJzdHJhdGUiLCA1KSwgcmVwKCJhbGdhZSIsOCkpKSAKc3BwLnNjcnNfc3ViIDwtIGNiaW5kKHZhcmlhYmxlX3R5cGUsIHNwcC5zY3JzX3N1YikKc3BwLnNjcnNfc3Vic3RyYXRlIDwtIHN1YnNldChzcHAuc2Nyc19zdWIsIHNwcC5zY3JzX3N1YiR2YXJpYWJsZV90eXBlID09ICJzdWJzdHJhdGUiKQpzcHAuc2Nyc19hbGdhZSA8LSBzdWJzZXQoc3BwLnNjcnNfc3ViLCBzcHAuc2Nyc19zdWIkdmFyaWFibGVfdHlwZSA9PSAiYWxnYWUiKQpgYGAKCiMjIyM8Zm9udCBjb2xvcj0icmVkIj5GaWd1cmUgMTwvZm9udD4KCmBgYHtyIG1lYW5fYml0ZV9maWd1cmUsIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBmaWcuY2FwID0gIkZpZ3VyZSAxOiBCcmF5IGN1cnRpcyBkaXN0YW5jZSBvbiBhbGwgdHJhaXRzOyBzdHJlc3MgPSAwLjA0Iiwgb3V0LndpZHRoID0gIjkwJSJ9CkZpZzEgPC0gZ2dwbG90KE5NRFMpICsKCWdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gTk1EUzEsIHkgPSBOTURTMiwgc2hhcGUgPSBTaXRlLCBjb2xvdXIgPSBTcGVjaWVzKSwgc2l6ZSA9IDQpKwoJc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBzYW1wX3BhbCkgKwoJZ2VvbV9zZWdtZW50KGFlcyh4PTAsIHk9MCwgeGVuZD1TX01EUzEsIHllbmQ9U19NRFMyKSwgZGF0YSA9IHNwcC5zY3JzX3N1YnN0cmF0ZSwKICAgICAgCWFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjUsICJjbSIpKSwgY29sb3IgPSAicmVkIikgKwoJZ2VvbV90ZXh0KGRhdGEgPSBzcHAuc2Nyc19zdWJzdHJhdGUsIGFlcyh4ID0gU19NRFMxLCB5ID0gU19NRFMyLCBsYWJlbCA9IHNwZWNpZXMpLCAKCSAgICAgICAgICBudWRnZV95ID0gYygtMC4wMjUsIC0wLjAyNSwgLTAuMDUsIDAuMDUsIDAuMDUpKSArCglnZW9tX3NlZ21lbnQoYWVzKHg9MCx5PTAseGVuZD1TX01EUzEsIHllbmQ9U19NRFMyKSwgZGF0YSA9IHNwcC5zY3JzX2FsZ2FlLAogICAgICAJYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuNSwgImNtIikpLCBjb2xvciA9ICJibGFjayIpICsKCWdlb21fdGV4dChkYXRhID0gc3BwLnNjcnNfYWxnYWUsIGFlcyh4ID0gU19NRFMxLCB5ID0gU19NRFMyLCBsYWJlbCA9IHNwZWNpZXMpLCAKCSAgICAgICAgICBudWRnZV95ID0gYygtMC4wMjUsIDAuMDI1LCAwLjAyNSwgMC4wMjUsIDAuMDI1LCAtMC4wMjUsIC0wLjA1LCAtMC4wMjUpKSArCgl0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE1KQpGaWcxIDwtIEZpZzEgKyBjb29yZF9maXhlZCgpCkZpZzEKcGRmKCJSX09VVFBVVC9GaWd1cmVfMS5wZGYiKQpGaWcxCmludmlzaWJsZShkZXYub2ZmKCkpCmBgYAoKKioqCgo8YSBpZD0iUGFydCBJSTogRGF0YSBQcmVwYXJhdGlvbiI+PC9hPiAKCiMjUGFydCBJSTogRGF0YSBQcmVwYXJhdGlvbiAgCltiYWNrIHRvIHRvcF0oI2JhY2sgdG8gdG9wKQoKIyMjRGVmaW5lIGdyb3VwaW5ncwowaywgb24gdG8gdGhlIG1pY3JvYmlhbCBkYXRhLiBGaXJzdCwgd2UgbG9hZCB0aGUgZGF0YSBwYWNrZXQgcHJvZHVjZWQgYnkgdGhlIGZpbmFsIHN0ZXAgb2YgdGhlIERBREEyIHdvcmtmbG93LCBmb3JtYXQgc2FtcGxlIG5hbWVzLCBhbmQgZGVmaW5lIGdyb3VwaW5ncy4gV2Ugd2lsbCB1c2UgdGhlIHNhbXBsZSBuYW1lcyB0byBkZWZpbmUgdGhlIGRpZmZlcmVudCBncm91cHMuCgo+IEdyb3VwcwoKKiA1MyBpbmRpdmlkdWFscwoqIDMgZ2VuZXJhCiogNyBzcGVjaWVzCgpgYGB7ciBkZWxpbmlhdGVfc2FtcGxlX3R5cGVzfQpsb2FkKCJEQURBMl9EQVRBL2NvbWJvX3BpcGVsaW5lLnJkYXRhIikKc2FtcGxlcy5vdXQgPC0gcm93bmFtZXMoc2VxdGFiKQpzdWJqZWN0IDwtIHNhcHBseShzdHJzcGxpdChzYW1wbGVzLm91dCwgIltbOmRpZ2l0Ol1dIiksIGBbYCwgMSkKIyB0aGlzIHNwbGl0cyB0aGUgc3RyaW5nIGF0IGZpcnN0IGluc3RhbmNlIG9mIGEgZGlnaXQKc2FtcGxlX25hbWUgPC0gc3Vic3RyKHNhbXBsZXMub3V0LCAxLCA5OTkpICAjIHVzZSB0aGUgd2hvbGUgc3RyaW5nIGZvciBpbmRpdmlkdWFscwpnZW51cyA8LSBzdWJzdHIoc2FtcGxlcy5vdXQsIDEsIDIpICAjIHVzZSB0aGUgZmlyc3QgdHdvIGxldHRlcnMgZm9yIGdlbnVzCnNwZWNpZXMgPC0gc3Vic3RyKHNhbXBsZXMub3V0LCAxLCA1KSAgIyB1c2UgdGhlIG5leHQgdGhyZWUgbGV0dGVycyBmb3Igc3BlY2llcwpzYW1wbGVfbmFtZQp1bmlxdWUoZ2VudXMpCnVuaXF1ZShzcGVjaWVzKQpgYGAKCkFuZCBmaW5hbGx5IHdlIGRlZmluZSBhIHNhbXBsZSBkYXRhIGZyYW1lIHRoYXQgaG9sZHMgdGhlIGRpZmZlcmVudCBncm91cHMgd2UgZXh0cmFjdGVkIGZyb20gdGhlIHNhbXBsZSBuYW1lcy4gT24gdGhlIHJpZ2h0IGFyZSBhIGZldyBzYW1wbGVzIGFuZCB0aGVpciBkaWZmZXJlbnQgZ3JvdXBzIG5hbWVzLiAKCmBgYHtyIGRlZmluZV92YXJpYWJsZXN9CiNkZWZpbmUgYSBzYW1wbGUgZGF0YSBmcmFtZQpzYW1kZiA8LSBkYXRhLmZyYW1lKFNhbU5hbWUgPSBzYW1wbGVfbmFtZSwgR2VuID0gZ2VudXMsIFNwID0gc3BlY2llcykgIApyb3duYW1lcyhzYW1kZikgPC0gc2FtcGxlcy5vdXQKa2FibGUoc2FtZGZbYygxLCAxMywgMjAsIDMwLCA0NCksMTozXSwgcm93Lm5hbWVzID0gRkFMU0UpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGQUxTRSwgcG9zaXRpb24gPSAiZmxvYXRfcmlnaHQiKSAgJT4lCiAgY29sdW1uX3NwZWMoMTozLCB3aWR0aCA9ICIzLjVjbSIpCmBgYAoKCj4gQWJicmV2aWF0aW9uczogCgoqIEFjQ29lID0gKkFjYW50aHVydXMgY29ldWxldXMqCiogQWNUcmEgPSAqQWNhbnRodXJ1cyB0cmFjdHVzKgoqIFNjVGFlID0gKlNjYXJ1cyB0YWVuaW9wdGVydXMqCiogU3BBdXIgPSAqU3Bhcmlzb21hIGF1cm9mcmVuYXR1bSoKKiBTcFZpciA9ICpTcGFyaXNvbWEgdmlyaWRlKgoqIFNjVmV0ID0gKlNjYXJ1cyB2ZXR1bGEqCiogU3BDaHIgPSAqU3Bhcmlzb21hIGNocnlzb3B0ZXJ1bSoKCgojIyNDcmVhdGUgJiBtb2RpZnkgYSBwaHlsb3NlcSBvYmplY3QKCk5leHQgd2UgY3JlYXRlIGEgcGh5bG9zZXEgKHBzKSBvYmplY3Qgd2l0aCB0aGUgU2lsdmEgKHNsdikgdGF4b25vbXkuIFRoZXJlIGlzIGFsc28gYSBHcmVlbmdlbmVzIChnZykgYW5ub3RhdGlvbiBpbiB0aGUgb3V0cHV0IGZpbGUgZnJvbSBEQURBMiB3aGljaCBjYW4gYmUgdXNlZCBpbnN0ZWFkIG9mICB0aGUgU2lsdmEgYW5ub3RhdGlvbi4gSnVzdCBjaGFuZ2UgYHRheF9zaWx2YWAgdG8gYHRheF9nZ2AuIEF0IHRoaXMgcG9pbnQgd2UgcmVuYW1lIHRoZSBhbXBsaWNvbiBzZXF1ZW5jZSB2YXJpYW50cyAoQVNWcykgc28gdGhlIGRlc2lnbmF0aW9ucyBhcmUgYSBiaXQgbW9yZSB1c2VyIGZyaWVuZGx5LiBCeSBkZWZhdWx0LCBEQURBMiBuYW1lcyBlYWNoIEFTViBieSBpdHMgdW5pcXVlIHNlcXVlbmNlIHNvIHRoYXQgZGF0YSBjYW4gYmUgZGlyZWN0bHkgY29tcGFyZWQgYWNyb3NzIHN0dWRpZXMgKHdoaWNoIGlzIGdyZWF0KS4gQnV0IHRoaXMgY29udmVudGlvbiBjYW4gIGdldCBjdW1iZXJzb21lIGRvd25zdHJlYW0sIHNvIHdlIHJlbmFtZSB0aGUgQVNWcyB1c2luZyBhIHNpbXBsZXIgY29udmVudGlvbi0tLUFTVjEsIEFTVjIsIEFTVjMsIGFuZCBzbyBvbiwgd2hpbGUgcmV0YWluaW5nIHRoZSBleGFjdCBzZXF1ZW5jZXMuIAoKYGBge3IgY3JlYXRlX3BzX29iamVjdH0KcHNfc2x2IDwtIHBoeWxvc2VxKG90dV90YWJsZShzZXF0YWIsIHRheGFfYXJlX3Jvd3MgPSBGQUxTRSksIHNhbXBsZV9kYXRhKHNhbWRmKSwgCiAgICB0YXhfdGFibGUodGF4X3NpbHZhKSkgIyB0aGlzIGNyZWF0ZSB0aGUgcGh5bG9zZXEgb2JqZWN0CnRheF90YWJsZShwc19zbHYpIDwtIGNiaW5kKHRheF90YWJsZShwc19zbHYpLCByb3duYW1lcyh0YXhfdGFibGUocHNfc2x2KSkpCnRheGFfbmFtZXMocHNfc2x2KSA8LSBwYXN0ZTAoIkFTViIsIHNlcShudGF4YShwc19zbHYpKSkgIyBhZGRpbmcgdW5pcXVlIEFTViBuYW1lcwp0YXhfdGFibGUocHNfc2x2KSA8LSBjYmluZCh0YXhfdGFibGUocHNfc2x2KSwgcm93bmFtZXModGF4X3RhYmxlKHBzX3NsdikpKQpoZWFkKHRheGFfbmFtZXMocHNfc2x2KSkKcHNfc2x2CmBgYAoKQW5kIHRoZW4gd2UgYWRkIHR3byBmaW5hbCBjb2x1bW5zIHdpdGggdGhlIGFjdHVhbCBBU1Ygc2VxdWVuY2VzIGFuZCBBU1YgSURzLiBUaGlzIHdpbGwgYmUgdXNlZnVsIGxhdGVyIHdoZW4gdHJ5aW5nIHRvIGV4cG9ydCBhIGZhc3RhIGZpbGUuCgpgYGB7ciBhZGRfQVNWX2NvdWxtbn0KY29sbmFtZXModGF4X3RhYmxlKHBzX3NsdikpIDwtIGMoIktpbmdkb20iLCAiUGh5bHVtIiwgIkNsYXNzIiwgIk9yZGVyIiwgCiAgICAiRmFtaWx5IiwgIkdlbnVzIiwgIkFTVl9TRVEiLCAiQVNWX0lEIikKYGBgCgpBdCB0aGlzIHBvaW50IHdlIGhhdmUgYSBjb21wbGV0ZWx5IHVuYWR1bHRlcmF0ZWQgcGh5bG9zZXEgb2JqZWN0IGJlY2F1c2UgaXQgY29udGFpbnMgIGFsbCB0aGUgQVNWcyBhbmQgYWxsIHNhbXBsZXMuICBMZXRzIGV4cG9ydCB0aGUgc2VxdWVuY2UgYW5kIHRheG9ub215IHRhYmxlcyBmb3IgcG9zdGVyaXR5IHNha2UuIAoKYGBge3IgZXhwb3J0X3NlcV90YXhfdGFibGVzfQp3cml0ZS50YWJsZSh0YXhfdGFibGUocHNfc2x2KSwgIlJfT1VUUFVUL2Z1bGxfdGF4X3RhYmxlLnR4dCIsIHNlcD0iXHQiLCBxdW90ZSA9IEZBTFNFLCBjb2wubmFtZXM9TkEpCndyaXRlLnRhYmxlKHQob3R1X3RhYmxlKHBzX3NsdikpLCAiUl9PVVRQVVQvZnVsbF9zZXFfdGFibGUudHh0Iiwgc2VwPSJcdCIsIHF1b3RlID0gRkFMU0UsIGNvbC5uYW1lcz1OQSkKd3JpdGUudGFibGUoc2FtcGxlX2RhdGEocHNfc2x2KSwgIlJfT1VUUFVUL2Z1bGxfc2FtcGxlX2RhdGEudHh0Iiwgc2VwPSJcdCIsIHF1b3RlID0gRkFMU0UsIHJvdy5uYW1lcyA9ICBGQUxTRSkKYGBgCgpSZW1lbWJlciB0aHJlZSBvZiB0aGVzZSBzYW1wbGVzIHdlcmUgb21pdHRlZCBiZWNhdXNlIHdlIGRpZCBub3QgaGF2ZSByZXBsaWNhdGVzIGZvciB0aGUgaG9zdCBzcGVjaWVzLiBMZXRzIHJlbW92ZSB0aG9zZSBzYW1wbGVzLiBUaGUgb25seSB3YXkgd2UgY291bGQgZmlndXJlIG91dCBob3cgdG8gZG8gdGhpcyB3YXMgYnkgc2VsZWN0aW5nIHRoZSBzYW1wbGVzIHdlICp3YW50ZWQgdG8ga2VlcCouIElmIHlvdSB3YW50IHRvIGNoYW5nZSB0aGUgZ3JvdXAgb2Ygc2FtcGxlcywgbW9kaWZ5IHRoZSBzY3JpcHQgYWNjb3JkaW5nbHkuCgpgYGB7ciBzZWxlY3Rfc2FtcGxlc30KcHNfc2x2X2Jhc2UgPC0gcHJ1bmVfc2FtcGxlcyhjKCJTcEF1cjAxIiwgIlNwQXVyMDIiLCAiU3BBdXIwMyIsICJTcEF1cjA0IiwgCiAgICAiU3BBdXIxMCIsICJTcEF1cjExIiwgIlNwQXVyMTIiLCAiU3BBdXIxMyIsICJTcFZpcjAxIiwgIlNwVmlyMDIiLCAiU3BWaXIwMyIsIAogICAgIlNwVmlyMDQiLCAiU3BWaXIwNSIsICJTcFZpcjA2IiwgIlNwVmlyMDciLCAiU3BWaXIwOCIsICJTcFZpcjA5IiwgIlNwVmlyMTAiLCAKICAgICJTcFZpcjExIiwgIkFjQ29lMDEiLCAiQWNDb2UwMiIsICJBY0NvZTAzIiwgIkFjQ29lMDQiLCAiQWNDb2UwNSIsICJBY0NvZTA2IiwgCiAgICAiQWNDb2UwNyIsICJBY0NvZTA4IiwgIkFjVHJhMDEiLCAiQWNUcmEwMiIsICJBY1RyYTAzIiwgIkFjVHJhMDQiLCAiQWNUcmEwNSIsIAogICAgIkFjVHJhMDYiLCAiQWNUcmEwNyIsICJBY1RyYTA4IiwgIkFjVHJhMDkiLCAiU2NUYWUwMSIsICJTY1RhZTAyIiwgIlNjVGFlMDMiLCAKICAgICJTY1RhZTA0IiwgIlNjVGFlMDUiLCAiU2NUYWUwNiIsICJTY1RhZTA3IiwgIlNjVGFlMDgiLCAiU2NUYWUwOSIsICJTcEF1cjA1IiwgCiAgICAiU3BBdXIwNiIsICJTcEF1cjA3IiwgIlNwQXVyMDgiLCAiU3BBdXIwOSIpLCBwc19zbHYpCnBzX3Nsdl9iYXNlCmBgYAoKMEssIHRocmVlIHNhbXBsZXMgZ29uZS4gQnV0IHdlIHByb2JhYmx5IGxvc3Qgc29tZSBBU1ZzIHdoZW4gdXNlIHdlIHJlbW92ZWQgc2FtcGxlcy4gU28gd2UgbmVlZCB0byBnZXQgcmlkIG9mIGFueSBBU1ZzIHRoYXQgaGF2ZSBub3cgYSB0b3RhbCBvZiAqKjAgcmVhZHMqKi4gVGhpcyB3aWxsIGJlIG91ciB3b3JraW5nIHBoeWxvc2VxIG9iamVjdC4gCgpgYGB7ciByZW1vdmVfQVNWX3dpdGhfemVyb3NfcmVhZHN9CnBzX3Nsdl93b3JrIDwtIHBydW5lX3RheGEodGF4YV9zdW1zKHBzX3Nsdl9iYXNlKSA+IDAsIHBzX3Nsdl9iYXNlKQpwc19zbHZfd29yawpgYGAKCkxvb2tzIGxpa2UgICoqNDM5IEFTVnMqKiAgd2VyZSBvbmx5IGZvdW5kIGluIHRob3NlIHRocmVlIHNhbXBsZXMuIAoKIyMjUmVtb3ZlIGNvbnRhbWluYW50cwoKVGhlc2Ugc2FtcGxlcyBhcmUgaW50ZXN0aW5hbCBjb21tdW5pdGllcyBhbmQgd2UgYXNzdW1lIHRoYXQgQ2hsb3JvcGxhc3QgYXJlIG5vdCBjb250cmlidXRpbmcgdG8gbWV0YWJvbGlzbS4gVGhlc2UgZGF0YSBjb3VsZCBiZSB1c2VmdWwgbGF0ZXIgYnV0IGZvciBub3cgbGV0cyBjcmVhdGUgYSBwaHlsb3NlcSBvYmplY3Qgd2l0aG91dCBDaGxvcm9wbGFzdC4KCjxmb250IHNpemU9IjMiIGNvbG9yPSJyZWQiPldBUk5JTkc8L2ZvbnQ+OiB0aGUgYHN1YnNldF90YXhhYCBjb21tYW5kICByZW1vdmVzIGFueXRoaW5nIHRoYXQgaXMgYE5BYCBmb3IgdGhlIHNwZWNpZmllZCB0YXhvbm9taWMgbGV2ZWwgb3IgYWJvdmUuICoqRm9yIGV4YW1wbGUqKiwgbGV0cyBzYXkgeW91IHJ1biAgdGhlICBgc3Vic2V0X3RheGFgIGNvbW1hbmQgdXNpbmcgYE9yZGVyICE9ICJDaGxvcm9wbGFzdCJgLiBTZWVtcyBsaWtlIHlvdSBzaG91bGQgZ2V0IGEgcGh5bG9zZXEgb2JqZWN0IHdpdGggZXZlcnl0aGluZyBleGNlcHQgIENobG9yb3BsYXN0LiBCdXQgYWN0dWFsbHkgdGhlIGNvbW1hbmQgbm90IG9ubHkgZ2V0cyByaWQgQ2hsb3JvcGxhc3QgYnV0IGV2ZXJ5dGhpbmcgIGVsc2UgdGhhdCBoYXMgYE5BYCBmb3IgT3JkZXIgYW5kIGFib3ZlLiBJbiBvdXIgZXhwZXJpZW5jZSB0aGlzIGlzIG5vdCB3ZWxsIGRvY3VtZW50ZWQgYW5kIHdlIGhhZCB0byAgZGlnIHRocm91Z2ggdGhlIGZpbGVzIHRvIGZpZ3VyZSBvdXQgd2hhdCB3YXMgaGFwcGVuaW5nLgoKT3VyIGRhdGFzZXQgaGFzIDU5MCBDaGxvcm9wbGFzdCBBU1ZzIGFuZCBydW5uaW5nIHRoZSAgY29tbWFuZCBhcyBpcyByZW1vdmVkIGFuIGFkZGl0aW9uYWwgMTI0NCBBU1ZzLiAgU28gbGV0cyBzZWUgaWYgd2UgY2FuIGdldCByaWQgb2YganVzdCBDaGxvcm9wbGFzdCBBU1ZzIHdpdGhvdXQgcmVtb3ZpbmcgZXZlcnl0aGluZyB0aGF0IGlzIHVuY2xhc3NpZmllZCBhdCBPcmRlciBhbmQgYWJvdmUuIFRvIGRvIHRoaXMsIHdlIHN1YnNldCB0aGUgdGF4YSB0byBnZW5lcmF0ZSBhIHBzIG9iamVjdCBvZiBqdXN0IENobG9yb3BsYXN0LCBzZWxlY3RlZCB0aGUgQVNWIGNvbHVtbiBvbmx5LCB0dXJuZWQgaXQgaW50byBhIGZhY3RvciwgYW5kIHVzZWQgdGhpcyB0byByZW1vdmUgQ2hsb3JvcGxhc3QgZnJvbSB0aGUgcHMgb2JqZWN0LiAKCmBgYHtyIHJlbW92ZV9jeWFub30KIyBnZW5lcmF0ZSBhIGZpbGUgd2l0aCBDaGxvcm9wbGFzdCBBU1ZzCkNIMSA8LSBzdWJzZXRfdGF4YShwc19zbHZfd29yaywgT3JkZXIgPT0gIkNobG9yb3BsYXN0IikKQ0gxIDwtICBhcyh0YXhfdGFibGUoQ0gxKSwgIm1hdHJpeCIpCkNIMSA8LSBDSDFbLCA4XQpDSDFkZiA8LSBhcy5mYWN0b3IoQ0gxKQpnb29kVGF4YUNIIDwtIHNldGRpZmYodGF4YV9uYW1lcyhwc19zbHZfd29yayksIENIMWRmKQpwc19zbHZfd29ya19ub19jeWFubyA8LSBwcnVuZV90YXhhKGdvb2RUYXhhQ0gsIHBzX3Nsdl93b3JrKQpwc19zbHZfd29ya19ub19jeWFubwpgYGAKClRoaXMgc3RlcCByZW1vdmVkICoqNTkwIENobG9yb3BsYXN0IEFTVnMqKi4gUGVyZmVjdC4KCkFuZCBub3cgd2UgdXNlIHRoZSBzYW1lIGFwcHJvYWNoIHRvIHJlbW92ZSBNaXRvY2hvbmRyaWEuIAoKYGBge3IgcmVtb3ZlX3NwZWNpZmljX3RheGF9CiMgZ2VuZXJhdGUgYSBmaWxlIHdpdGggbWl0b2Nob25kcmlhIEFTVnMKTVQxIDwtIHN1YnNldF90YXhhKHBzX3Nsdl93b3JrX25vX2N5YW5vLCBGYW1pbHkgPT0gIk1pdG9jaG9uZHJpYSIpCk1UMSA8LSAgYXModGF4X3RhYmxlKE1UMSksICJtYXRyaXgiKQpNVDEgPC0gTVQxWywgOF0KTVQxZGYgPC0gYXMuZmFjdG9yKE1UMSkKZ29vZFRheGEgPC0gc2V0ZGlmZih0YXhhX25hbWVzKHBzX3Nsdl93b3JrX25vX2N5YW5vKSwgTVQxZGYpCnBzX3Nsdl93b3JrX2ZpbHQgPC0gcHJ1bmVfdGF4YShnb29kVGF4YSwgcHNfc2x2X3dvcmtfbm9fY3lhbm8pCnBzX3Nsdl93b3JrX2ZpbHQKYGBgCgpTd2VldCwgbG9va3MgbGlrZSB0aGlzIHJlbW92ZWQgKiozMDYgTWl0b2Nob25kcmlhIEFTVnMqKi4gTmV4dCB3ZSBnZW5lcmF0ZSBzb21lIHN1bW1hcnkgZGF0YSBmb3IgZWFjaCBzYW1wbGUuCgpgYGB7ciBnZW5fc3RhdHMsIGV2YWwgPSBGQUxTRSwgaW5jbHVkZSA9IEZBTFNFfQojIGdlbmVyYWwgc3RhdHMgZm9yIHRoZSBkYXRhc2V0Lgpjb2xuYW1lcyh0YXhfdGFibGUocHNfc2x2X3dvcmtfZmlsdCkpCnNhbXBsZV9zdW1fZGYgPC0gZGF0YS5mcmFtZShzdW0gPSBzYW1wbGVfc3Vtcyhwc19zbHZfd29ya19maWx0KSkKdG90YWxfcmVhZHMgPC0gc3VtKG90dV90YWJsZShwc19zbHZfd29ya19maWx0KSkKc21pbiA8LSBtaW4oc2FtcGxlX3N1bXMocHNfc2x2X3dvcmtfZmlsdCkpCnNtZWFuIDwtIG1lYW4oc2FtcGxlX3N1bXMocHNfc2x2X3dvcmtfZmlsdCkpCnNtYXggPC0gbWF4KHNhbXBsZV9zdW1zKHBzX3Nsdl93b3JrX2ZpbHQpKQp0b3RhbF9yZWFkcwpzbWluCnNtYXgKc21lYW4KCiNTQ1JBVENIIHVzZSBmb3IgcXVpY2sgY29tcHV0ZSBvZiB0YXhhIGRpdmVyc2l0eQojcHNfc2x2X3dvcmtfZmlsdF9TQ1JBVENIIDwtIHN1YnNldF90YXhhKHBzX3Nsdl93b3JrLCBGYW1pbHkgPT0gIkVyeXNpcGVsb3RyaWNoYWNlYWUiKQojcHNfc2x2X3dvcmtfZmlsdF9TQ1JBVENICmBgYAoKIyMjUGh5bG9zZXEgb2JqZWN0IG1lcmdlZCBieSBzcGVjaWVzCgpPbmUgbGFzdCB0aGluZyB0byBkbyBpcyB0byBjcmVhdGUgYSBtZXJnZWQgcGh5bG9zZXEgb2JqZWN0IGdyb3VwZWQgYnkgaG9zdCBzcGVjaWVzLiBUaGlzIHdpbGwgY29tZSBpbiBoYW5keSBsYXRlciBmb3Igc29tZSBhbmFseXNlcy4gQmFzaWNhbGx5IHdlIGNvbGxhcHNlZCBhbGwgc2FtcGxlcyBmcm9tIHRoZSBzYW1lIGhvc3Qgc3BlY2llcyB0b2dldGhlci4KCmBgYHtyIG1lcmdlfQptZXJnZWRHUCA8LSBtZXJnZV9zYW1wbGVzKHBzX3Nsdl93b3JrX2ZpbHQsICJTcCIpClNEIDwtIG1lcmdlX3NhbXBsZXMoc2FtcGxlX2RhdGEocHNfc2x2X3dvcmtfZmlsdCksICJTcCIpCm1lcmdlZEdQCnNhbXBsZV9uYW1lcyhtZXJnZWRHUCkKYGBgCgpHcmVhdCwgc3RpbGwgdGhlIHNhbWUgbnVtYmVyIG9mIEFTVnMgYW5kIG5vdyBvbmx5IDUgInNhbXBsZXMiIGNvcnJlc3BvbmRpbmcgdG8gdGhlIDUgc3BlY2llcy4gCgpUaGVyZSBhcmUgbm93IHRoZSBzZXZlcmFsIHBoeWxvc2VxIG9iamVjdHMgdG8gY2hvc2UgZnJvbSBhbmQsIHVzaW5nIHRoZSBhYm92ZSBtZXRob2RzLCBhZGRpdGlvbmFsIG9iamVjdHMgY2FuIGVhc2lseSBiZSBjcmVhdGVkLiAKCiogYHBzX3NsdmAgLS0+IHBoeWxvc2VxIGRhdGFzZXQgd2l0aCBhbGwgNTMgc2FtcGxlcywgYWxsIEFTVnMuCiogYHBzX3Nsdl9iYXNlYCAtLT4gcGh5bG9zZXEgZGF0YXNldCB3aXRoIDUwIHNhbXBsZXMsIGFsbCBBU1ZzICh0aGlzIGlzIG5vdCB2ZXJ5IHVzZWZ1bCkuCiogYHBzX3Nsdl93b3JrYCAtLT4gcGh5bG9zZXEgZGF0YXNldCB3aXRoIDUwIHNhbXBsZXMsIDAgcmVhZCBBU1ZzIHJlbW92ZWQuCiogYHBzX3Nsdl93b3JrX2ZpbHRgIC0tPiBwaHlsb3NlcSBkYXRhc2V0IHdpdGggNTAgc2FtcGxlcywgQVNWcyBmcm9tIE1pdG9jaG9uZHJpYSBhbmQgQ3lhbm9iYWN0ZXJpYSByZW1vdmVkLgoqIGBtZXJnZWRHUGAgLS0+IGBwc19zbHZfd29ya19maWx0YCBwaHlsb3NlcSBkYXRhc2V0IGNvbGxhcHNlZCBieSBob3N0IHNwZWNpZXMuCgoKIyMjSG9zdCBJbmZvcm1hdGlvbgoKQmVmb3JlIHdlIGRvIGFueXRoaW5nIGVsc2UsIGxldHMgZ2VuZXJhdGUgc3VtbWFyeSBkYXRhIGZvciBlYWNoIGhvc3QuIFdlIGNhbiBnZW5lcmF0ZSBhICBzdW1tYXJ5IHJlcG9ydCBmb3IgYW55ICBwcyBvYmplY3QgYnV0IHdlIHdpbGwgdXNlIHRoZSBvYmplY3Qgd2l0aCBtaXRvY2hvbmRyaWEgYW5kIGNobG9ycGxhc3RzIHJlbW92ZWQsIGFzIHdlbGwgYXMgdGhlIGxvdyByZXBsaWNhdGUgaG9zdCBzcGVjaWVzIHJlbW92ZWQuIFdlIHdpbGwgYWxzbyBhZGQgZGV0YWlscyBhYm91dCBlYWNoIGhvc3QuIFRoZSB0YWJsZSBpcyBkaXNwbGF5ZWQgYmVsb3cuIFdlIGNhbiB1c2UgdGhlc2UgZGF0YSB3aGVuIHdlIHVwbG9hZCB0aGUgb3JpZ2luYWwgZmFzdHEgZmlsZXMgdG8gc2VxdWVuY2UgcmVhZCBhcmNoaXZlcy4gTGF0ZXIgb24gd2Ugd2lsbCBhbHNvIGFkZCBhbHBoYSBkaXZlcnNpdHkgc3RhdHMgYW5kIHNhdmUgdGhlIHRhYmxlLgoKKioqCgojIyMjIEhvc3QgZGV0YWlscwoKYGBge3Igc2FtcGxlX3N1bW1hcnlfdGFibGUsIHdhcm5pbmcgPSBGQUxTRSwgZmlnLmFsaWduID0gImNlbnRlciJ9CnRvdGFsX3JlYWRzIDwtIHNhbXBsZV9zdW1zKHBzX3Nsdl93b3JrX2ZpbHQpCnRvdGFsX3JlYWRzIDwtIGFzLmRhdGEuZnJhbWUodG90YWxfcmVhZHMsIG1ha2UubmFtZXMgPSBUUlVFKQp0b3RhbF9yZWFkcyA8LSB0b3RhbF9yZWFkcyAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJob3N0X0lEIikKCnRvdGFsX2FzdnMgPC0gZXN0aW1hdGVfcmljaG5lc3MocHNfc2x2X3dvcmtfZmlsdCwgbWVhc3VyZXMgPSAiT2JzZXJ2ZWQiKQp0b3RhbF9hc3ZzIDwtIHRvdGFsX2FzdnMgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiaG9zdF9JRCIpCgpzYW1fZGV0YWlscyA8LSBzYW1wbGVfZGF0YShwc19zbHYpCnNhbV9kZXRhaWxzIDwtIHNhbV9kZXRhaWxzICU+JSBtdXRhdGUoZ2VudXMgPSBjYXNlX3doZW4oCiAgICBHZW4gPT0gIkFjIiB+ICJBY2FudGh1cnVzIiwgCiAgICBHZW4gPT0gIlNjIiB+ICJTY2FydXMiLAogICAgR2VuID09ICJTcCIgfiAiU3Bhcmlzb21hIikpCgpzYW1fZGV0YWlscyA8LSBzYW1fZGV0YWlscyAlPiUgbXV0YXRlKHNwZWNpZXMgPSBjYXNlX3doZW4oCiAgICBTcCA9PSAiQWNDb2UifiAiY29lcnVsZXVzIiwKICAgIFNwID09ICJBY1RyYSJ+ICJ0cmFjdHVzIiwKICAgIFNwID09ICJTY1RhZSJ+ICJ0YWVuaW9wdGVydXMiLAogICAgU3AgPT0gIlNwQXVyIn4gImF1cm9mcmVuYXR1bSIsCiAgICBTcCA9PSAiU3BWaXIifiAidmlyaWRlIikpCiNTcCA9PSAiU3BDaHIifiAiY2hyeXNvcHRlcnVtIiwKI1NwID09ICJTY1ZldCJ+ICJ2ZXR1bGEiCnNhbV9kZXRhaWxzIDwtIHNhbV9kZXRhaWxzICU+JSBtdXRhdGUoY29tbW9uX25hbWUgPSBjYXNlX3doZW4oCiAgICBTcCA9PSAiQWNDb2UiIH4gImJsdWUgdGFuZyBzdXJnZW9uZmlzaCIsIAogICAgU3AgPT0gIkFjVHJhIiB+ICJmaXZlYmFuZCBzdXJnZW9uZmlzaCIsIAogICAgU3AgPT0gIlNjVGFlIiB+ICJwcmluY2VzcyBwYXJyb3RmaXNoIiwgCiAgICBTcCA9PSAiU3BBdXIiIH4gInJlZGJhbmQgcGFycm90ZmlzaCIsIAogICAgU3AgPT0gIlNwVmlyIiB+ICJzdG9wbGlnaHQgcGFycm90ZmlzaCIpKQoKI1NwID09ICJTcENociIgfiAicmVkdGFpbCBwYXJyb3RmaXNoIiwKI1NwID09ICJTY1ZldCIgfiAicXVlZW4gcGFycm90ZmlzaCIpKQoKc2FtX2RldGFpbHMgPC0gc2FtX2RldGFpbHMgJT4lIG11dGF0ZShOQ0JJX3R4aWQgPSBjYXNlX3doZW4oCiAgICBTcCA9PSAiQWNDb2UiIH4gIjE1NzU4NSIsIAogICAgU3AgPT0gIkFjVHJhIiB+ICIxMzE2MDEzIiwgCiAgICBTcCA9PSAiU2NUYWUiIH4gIjU0NDQxOCIsIAogICAgU3AgPT0gIlNwQXVyIiB+ICI1OTY2MyIsIAogICAgU3AgPT0gIlNwVmlyIiB+ICI1OTY2NiIpKQojU3AgPT0gIlNwQ2hyIiB+ICI1MTc2NiIsCiNTcCA9PSAiU2NWZXQiIH4gIjg0NTQzIikpCgpzYW1fZGV0YWlscyA8LSBzYW1fZGV0YWlsc1stYygyLDMpXQpjb2xuYW1lcyhzYW1fZGV0YWlscykgPC0gYygiaG9zdF9JRCIsICJob3N0X2dlbnVzIiwgImhvc3Rfc3BlY2llcyIsIAogICAgImZ1bGxfbmFtZSIsICJOQ0JJX3R4aWQiKQoKbWVyZ2VfdGFiIDwtIG1lcmdlKHNhbV9kZXRhaWxzLCB0b3RhbF9yZWFkcywgYnkgPSAiaG9zdF9JRCIpCm1lcmdlX3RhYjIgPC0gbWVyZ2UobWVyZ2VfdGFiLCB0b3RhbF9hc3ZzLCBieSA9ICJob3N0X0lEIikKY29sbmFtZXMobWVyZ2VfdGFiMikgPC0gYygiaG9zdF9JRCIsICJob3N0X2dlbnVzIiwgImhvc3Rfc3BlY2llcyIsIAogICAgImNvbW1vbl9uYW1lIiwgIk5DQklfdHhpZCIsICAidG90YWxfcmVhZHMiLCAidG90YWxfQVNWcyIpCgojIFdlIGFsc28gaGF2ZSBhIGRhdGF0YWJsZSBjb250YWluaW5nIG1ldHJpY3MgZm9yIGVhY2ggaG9zdC4gTGV0cyBicmluZyB0aGlzIGluIAojIGFuZCBtZXJnZSB3aXRoICB0aGUgc3VtbWFyeSB0YWJsZQptZXRyaWNzIDwtIHJlYWQudGFibGUoIk1BTlVBTF9JTlBVVC9ob3N0X21ldHJpY3MudHh0Iiwgc2VwID0gIlx0IiwgaGVhZGVyID0gVFJVRSkKaG9zdF9kZXRhaWxzIDwtIG1lcmdlKG1lcmdlX3RhYjIsIG1ldHJpY3MsIGJ5ID0gImhvc3RfSUQiKQpjb2xuYW1lcyhob3N0X2RldGFpbHMpIDwtIGMoImhvc3RfSUQiLCAiaG9zdF9nZW51cyIsICJob3N0X3NwZWNpZXMiLCAKICAgICAgICAgICAiY29tbW9uX25hbWUiLCAiTkNCSV90eGlkIiwgICJ0b3RhbF9yZWFkcyIsICJ0b3RhbF9BU1ZzIiwgCiAgICAgICAgICAgImNvbGxlY3Rpb25fZGF0ZSIsICJwaGFzZSIsICJ3ZWlnaHQiLCAidG90YWxfbGVuZ3RoIiwgCiAgICAgICAgICAgImZvcmVndXRfbGVuZ3RoIiwgIm1pZGd1dF9sZW5ndGgiLCAKICAgICAgICAgICAiaGluZGd1dF9sZW5ndGgiLCAidG90YWxfZ3V0X2xlbmd0aCIpCgpkYXRhdGFibGUoaG9zdF9kZXRhaWxzLCByb3duYW1lcyA9IEZBTFNFLCB3aWR0aCA9ICIxMDAlIiwgY29sbmFtZXMgPSBjKAogICAgICAgICJob3N0X0lEIiwgImhvc3RfZ2VudXMiLCAiaG9zdF9zcGVjaWVzIiwgImNvbW1vbl9uYW1lIiwgIk5DQklfdHhpZCIsICAKICAgICAgICAidG90YWxfcmVhZHMiLCAidG90YWxfQVNWcyIsICJDb2xsZWN0aW9uX2RhdGUiLCAiUGhhc2UiLCAiV2VpZ2h0IChnKSIsIAogICAgICAgICJUb3RhbCBsZW5ndGggKGNtKSIsICJGb3JlIGd1dCBsZW5ndGggKGNtKSIsICJNaWQgZ3V0IGxlbmd0aCAoY20pIiwgCiAgICAgICAgIkhpbmQgZ3V0IGxlbmd0aCAoY20pIiwgIlRvdGFsIGd1dCBsZW5ndGggKGNtKSIpLCAKICAgIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbihzdHlsZSA9ICJjYXB0aW9uLXNpZGU6IGJvdHRvbTsgdGV4dC1hbGlnbjogbGVmdDsiLCAKICAgICJUYWJsZTogIiwgaHRtbHRvb2xzOjplbSgiU2FtcGxlIHN1bW1hcnkuIikpLCBleHRlbnNpb25zID0gIkJ1dHRvbnMiLCAKICAgIG9wdGlvbnMgPSBsaXN0KGNvbHVtbkRlZnMgPSBsaXN0KGxpc3QoY2xhc3NOYW1lID0gImR0LWxlZnQiLCB0YXJnZXRzID0gMCkpLCAKICAgICAgICBkb20gPSAiQmxmcnRpcCIsIHBhZ2VMZW5ndGggPSA1LCBsZW5ndGhNZW51ID0gYyg1LCAxMCwgMjUsIDUwKSwgYnV0dG9ucyA9IGMoImNzdiIsIAogICAgICAgICAgICAiY29weSIpLCBzY3JvbGxYID0gVFJVRSwgc2Nyb2xsQ29sbGFwc2UgPSBUUlVFKSkKYGBgCgpOb3cgd2UgaGF2ZSBhIG5pY2UgbGl0dGxlIHN1bW1hcnkgdGFibGUgYWJvdXQgZWFjaCBzYW1wbGUtLS1nZW51cy9zcGVjaWVzLCBjb21tb24gbmFtZSwgbnVtYmVyIG9mIHJlYWRzLCBudW1iZXIgb2YgQVNWcywgZXRjLiBBbGwgb2YgdGhpcyBpbmZvIGNhbiBiZSB1c2VkIHdoZW4gc3VibWl0dGluZyBzYW1wbGVzIHRvIHNlcXVlbmNlIHJlYWQgYXJjaGl2ZXMuIE9uY2Ugd2UgY29uZHVjdCBhbHBoYSBkaXZlcnNpdHkgZXN0aW1hdGVzIGJlbG93LCB3ZSB3aWxsIGFkZCB0aGF0IGRhdGEgdG8gdGhlIHRhYmxlIGFib3ZlIGFuZCBleHBvcnQgYXMgKipTdXBwbGVtZW50YXJ5IFRhYmxlIDMqKi4KCioqKgoKPGEgaWQ9IlBhcnQgSUlJOiBDb21tdW5pdHkgQ29tcG9zaXRpb24gJiBEaXZlcnNpdHkiPjwvYT4gIAoKIyNQYXJ0IElJSTogQ29tbXVuaXR5IENvbXBvc2l0aW9uICYgRGl2ZXJzaXR5IHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQoKW2JhY2sgdG8gdG9wXSgjYmFjayB0byB0b3ApCgo+IFdoYXQgYXJlIHRoZSBkb21pbmFudCB0YXhhIGluIHRoaXMgc3lzdGVtPyBIb3cgZGl2ZXJzZSBhcmUgdGhlc2UgY29tbXVuaXRpZXM/IEhvdyBzaW1pbGFyIGFyZSBzYW1wbGVzIHRvIGVhY2ggb3RoZXI/IAoKCkJlZm9yZSB3ZSBjYW4gc3RhcnQgdG8gdW5kZXJzdGFuZCBhIHN5c3RlbSwgd2UgbmVlZCB0byBrbm93IHNvbWV0aGluZyBhYm91dCBpdHMgcGFydHMuIFNvIGxldHMgc3RhcnQgd2l0aCBhIHF1aWNrIGxvb2sgYXQgY2xhc3MtbGV2ZWwgZGl2ZXJzaXR5LiBPZiBjb3Vyc2UsIHlvdSBjYW4gY2hhbmdlIHRoaXMgdG8gYW55IHRheG9ub21pYyByYW5rIHlvdSB3aXNoLiBIZXJlIHdlIGNyZWF0ZWQgYSAqKnNvcnRhYmxlKiogdGFibGUgdGhhdCBoYXMgdGhlIHRvdGFsIG51bWJlciBvZiByZWFkcyBhbmQgQVNWcyBmb3IgZWFjaCBjbGFzcwoKKioqCgojIyMjICoqQ29tbXVuaXR5IENvbXBvc2l0aW9uKioKCiMjIyMgVGF4b25vbWljIGNvbXBvc2l0aW9uOiBUb3RhbCByZWFkcyAmIEFTVnMgYnkgQ2xhc3MKCiMjIyM8Zm9udCBjb2xvcj0icmVkIj5TdXBwbGVtZW50YXJ5IFRhYmxlIDQ8L2ZvbnQ+CgpgYGB7ciBkaXZlcnNpdHlfdGFibGUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQojIGdlbmVyYXRlIHRoZSBBU1YgdGFibGUKdGF4X2FzdiA8LSB0YWJsZSh0YXhfdGFibGUocHNfc2x2X3dvcmtfZmlsdClbLCAiQ2xhc3MiXSwgZXhjbHVkZSA9IE5VTEwsIAogICAgZG5uID0gIlRheGEiKQp0YXhfYXN2IDwtIGFzLmRhdGEuZnJhbWUodGF4X2FzdiwgbWFrZS5uYW1lcyA9IFRSVUUpCiMgZ2VuZXJhdGUgdGhlIHJlYWRzIHRhYmxlCnRheF9yZWFkcyA8LSBmYWN0b3IodGF4X3RhYmxlKHBzX3Nsdl93b3JrX2ZpbHQpWywgIkNsYXNzIl0pCnRheF9yZWFkcyA8LSBhcHBseShvdHVfdGFibGUocHNfc2x2X3dvcmtfZmlsdCksIE1BUkdJTiA9IDEsIGZ1bmN0aW9uKHgpCnsKICAgIHRhcHBseSh4LCBJTkRFWCA9IHRheF9yZWFkcywgRlVOID0gc3VtLCBuYS5ybSA9IFRSVUUsIHNpbXBsaWZ5ID0gVFJVRSkKfSkKdGF4X3JlYWRzIDwtIGFzLmRhdGEuZnJhbWUodGF4X3JlYWRzLCBtYWtlLm5hbWVzID0gVFJVRSkKdGF4X3JlYWRzIDwtIGNiaW5kKHRheF9yZWFkcywgcmVhZHMgPSByb3dTdW1zKHRheF9yZWFkcykpCnRheF9yZWFkcyA8LSB0YXhfcmVhZHNbNTFdCnRheF9yZWFkcyA8LSBzZXREVCh0YXhfcmVhZHMsIGtlZXAucm93bmFtZXMgPSBUUlVFKVtdCiMgbWVyZ2UgdGhlIHR3byB0YWJsZXMgYW5kIG1ha2UgZXZlcnl0aGluZyBsb29rIHByZXR0eQojIGluIGFuIGludGVyYWN0aXZlIHRhYmxlCnRheGFfcmVhZF9hc3ZfdGFiIDwtIG1lcmdlKHRheF9yZWFkcywgdGF4X2FzdiwgYnkueCA9ICJybiIsIGJ5LnkgPSAiVGF4YSIpCm5hbWVzKHRheGFfcmVhZF9hc3ZfdGFiKSA8LSBjKCJUYXhhIiwgInRvdGFsIHJlYWRzIiwgInRvdGFsIEFTVnMiKQoKd3JpdGUudGFibGUodGF4YV9yZWFkX2Fzdl90YWIsICJSX09VVFBVVC9TdXBwbGVtZW50YXJ5X1RhYmxlXzQudHh0Iiwgc2VwID0gIlx0IiwgCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQoKZGF0YXRhYmxlKHRheGFfcmVhZF9hc3ZfdGFiLCByb3duYW1lcyA9IEZBTFNFLCB3aWR0aCA9ICIxMDAlIiwgY29sbmFtZXMgPSBjKAogICAgICAgICAgICAiVGF4YSIsICJ0b3RhbCByZWFkcyIsICJ0b3RhbCBBU1ZzIiksIAogICAgICAgICAgICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oCiAgICAgICAgICAgICAgc3R5bGUgPSAiY2FwdGlvbi1zaWRlOiBib3R0b207IHRleHQtYWxpZ246IGxlZnQ7IiwgCiAgICAiU3VwcGxlbWVudGFyeSBUYWJsZSA0OiAiLCBodG1sdG9vbHM6OmVtKCJUb3RhbCByZWFkcyAmIEFTVnMgYnkgQ2xhc3MiKSksIGV4dGVuc2lvbnMgPSAiQnV0dG9ucyIsIAogICAgb3B0aW9ucyA9IGxpc3QoY29sdW1uRGVmcyA9IGxpc3QobGlzdChjbGFzc05hbWUgPSAiZHQtbGVmdCIsIHRhcmdldHMgPSAwKSksIAogICAgICAgIGRvbSA9ICJCbGZydGlwIiwgcGFnZUxlbmd0aCA9IDUsIGxlbmd0aE1lbnUgPSBjKDUsIDEwLCAzNSwgNzApLCBidXR0b25zID0gYygiY3N2IiwgCiAgICAgICAgICAgICJjb3B5IikpKQpgYGAKCgpMb29rcyBsaWtlIFByb3Rlb2JhY3RlcmlhLCBGaXJtaWN1dGVzLCBGdXNvYmFjdGVyaWEsIFBsYW5jdG9teWNldGVzLCBhbmQgQmFjdGVyb2lkZXRlcyBkb21pbmF0ZSBpbiB0aGUgcmVhZCBkZXBhcnRtZW50LiBDdXJpb3VzbHksIEZ1c29iYWN0ZXJpYSBoYXMgY29tcGFyYXRpdmVseSBsb3cgQVNWIHJpY2huZXNzLiAKCioqKgoKTXVjaCBvZiB0aGUgYW5hbHlzZXMgd2UgZG8gZnJvbSBoZXJlIG9uIG91dCB3aWxsIGJlIGF0IHRoZSAqKkNsYXNzKiogJiAqKkZhbWlseSoqIGxldmVscy4gV2UgY2hvc2Ugbm90IHRvIGZvY3VzIG9uIHRoZSBHZW51cyBsZXZlbCBiZWNhdXNlIHRoZXJlIHNpbXBseSBpcyBub3QgZW5vdWdoIHJlc29sdXRpb24gaW4gb3VyIGRhdGFzZXQgdG8gYnVpbGQgYSBjb2hlc2l2ZSBzdG9yeS4gVGhpcyBpcyBiZWNhdXNlIHRoZXNlIGZpc2ggYXJlIChtaWNyb2JpYWxseSkgdW5kZXJzdHVkaWVkICphbmQqIHdlIGFyZSBkZWFsaW5nIHdpdGggc2hvcnQgcmVhZCBkYXRhLiBPbiB0aGUgb3RoZXIgaGFuZCwgUGh5bHVtIGxldmVsIGlzIHRvbyBjb2Fyc2UgZm9yIGdyb3VwcyBsaWtlIFByb3Rlb2JhY3RlcmlhIGFuZCBGaXJtaWN1dGVzLiBPcmRlciBkaWQgbm90IHByb3ZpZGUgYW55IGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gYW5kIGNhbiBiZSBjdW1iZXJzb21lIGZvciB0YXhhIHdpdGggcG9vcmx5IHJlc29sdmVkIGxpbmVhZ2VzLiBEZXBlbmRpbmcgb24gdGhlIGRhdGFzZXQsIHlvdSBtYXkgd2FudCB0byBjaGFuZ2UgeW91ciBzdHJhdGVneS4gCgpMZXRzIHRha2UgYSBjbG9zZXIgbG9vayBDbGFzcy1sZXZlbCB0YXhvbm9taWMgY29udGVudCBvZiB0aGVzZSBjb21tdW5pdGllcy4gVGhlcmUgYXJlIG51bWVyb3VzIHdheXMgdG8gZG8gdGhpcyBidXQgaGVyZSB3ZSBjaG9zZSB0byBjb2xsYXBzZSBzYW1wbGVzIGJ5IGhvc3Qgc3BlY2llcyBhbmQgZGlzcGxheSB0aGUgcmVsYXRpdmUgYWJ1bmRhbmNlIG9mIHRoZSBtb3N0IGRvbWluYW50IHRheGEuIFdlIGFsc28gZ2VuZXJhdGVkIGFuIGFsdGVybmF0aXZlIHZpZXcgb2YgdGF4b25vbWljIGNvbXBvc2l0aW9uIGZvciBpbmRpdmlkdWFsIHNhbXBsZXMtLS1bc2VwYXJhdGUgYmFyIHBsb3RzXSgjc2VwYXJhdGUtYmFyLXBsb3RzKSB0aGF0IGFyZSBpbmNsdWRlZCBhcyAqKlN1cHBsZW1lbnRhcnkgRmlndXJlIDEqKiAoc2VlIGJlbG93IGZvciBjb2RlKS4gCgpTdGFja2VkIGJhciBjaGFydHMgYXJlIG5vdCB0aGUgYmVzdCBidXQgd2UgbGlrZSB0aGVtIGZvciBhIGJpcmRzIGV5ZSB2aWV3IG9mIHRoZSBkYXRhLiBIZXJlIHdlIGNhbGN1bGF0ZSB0aGUgcmVsYXRpdmUgYWJ1bmRhbmNlIG9mIHRheGEgZm9yIGVhY2ggaG9zdCBzcGVjaWVzIGF0IHRoZSAqKkNsYXNzKiogbGV2ZWwuIEl0IHR1cm5zIG91dCB0aGlzIGlzIG5vdCB0b28gZWFzeSBpbiBwaHlsb3NlcSBhbmQgdGhlcmUgaXMgYSBsb3Qgb2YgKG1lc3N5KSBjb2RlLiAKCjxhIGlkPSJyZWxhdGl2ZSBhYnVuZGFuY2UgcGh5bG9zZXEgb2JqZWN0Ij48L2E+CgpgYGB7ciBjYWxjX3JlbF9hYnVuZF9hbmRfbWVyZ2V9CiMgY2FsY3VsYXRlIHRoZSBhdmVyYWdlcyBhbmQgbWVyZ2UgYnkgc3BlY2llcwpwc19zbHZfZmlsdF9BVkcgPC0gdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMocHNfc2x2X3dvcmtfZmlsdCwgZnVuY3Rpb24oeCkgeC9zdW0oeCkpCm1lcmdlZEdQX0JBUiA8LSBtZXJnZV9zYW1wbGVzKHBzX3Nsdl9maWx0X0FWRywgIlNwIikKU0RfQkFSIDwtIG1lcmdlX3NhbXBsZXMoc2FtcGxlX2RhdGEocHNfc2x2X2ZpbHRfQVZHKSwgIlNwIikKCiMgbWVyZ2UgdGF4YSBieSByYW5rLiBJZiB5b3UgY2hvb3NlIGEgZGlmZmVyZW50IHJhbmsgYmUgc3VyZSB0byBjaGFuZ2UKIyB0aGUgcmFuayB0aHJvdWdob3V0IHRoaXMgY29kZSBjaHVuawptZGF0YV9waHkgPC0gdGF4X2dsb20obWVyZ2VkR1BfQkFSLCB0YXhyYW5rID0gIkNsYXNzIiwgTkFybSA9IEZBTFNFKSAgCm1kYXRhX3BoeXJlbCA8LSB0cmFuc2Zvcm1fc2FtcGxlX2NvdW50cyhtZGF0YV9waHksIGZ1bmN0aW9uKHgpIHgvc3VtKHgpKQptZWx0ZCA8LSBwc21lbHQobWRhdGFfcGh5cmVsKQptZWx0ZCRDbGFzcyA8LSBhcy5jaGFyYWN0ZXIobWVsdGQkQ2xhc3MpCgojIGNhbGN1bGF0ZSB0aGUgdG90YWwgcmVsYXRpdmUgYWJ1bmRhbmNlIGZvciBhbGwgdGF4YQptZWFucyA8LSBkZHBseShtZWx0ZCwgfkNsYXNzLCBmdW5jdGlvbih4KSBjKG1lYW4gPSBtZWFuKHgkQWJ1bmRhbmNlKSkpCm1lYW5zJG1lYW4gPC0gcm91bmQobWVhbnMkbWVhbiwgZGlnaXRzID0gOCkKdGF4YV9tZWFucyA8LSBtZWFuc1tvcmRlcigtbWVhbnMkbWVhbiksIF0gICMgdGhpcyBvcmRlciBpbiBkZWNlbmRpbmcgZmFzaGlvbgp0YXhhX21lYW5zIDwtIGZvcm1hdCh0YXhhX21lYW5zLCBzY2llbnRpZmljID0gRkFMU0UpICAjIGRpdGNoIHRoZSBzY2kgbm90YXRpb24gCmBgYAoKU2luY2Ugb3VyIGdvYWwgaXMgdG8gZ2VuZXJhdGUgYSBmaWd1cmUgYW5kIHdlIG9ubHkgaGF2ZSA5IGNvbG9ycywgc29tZSB0YXhhIHdpbGwgbmVlZCB0byBiZSBwdXQgaW50byBhbiAqKk90aGVyKiogY2F0ZWdvcnkuIFdlIGNhbiBkZWZpbmUgJ090aGVyJyBob3dldmVyIHdlIGxpa2Ugc28gbGV0cyB0YWtlIGEgbG9vayBhdCB0aGUgb3ZlcmFsbCByZWxhdGl2ZSBhYnVuZGFuY2Ugb2YgZWFjaCBDbGFzcy4gCgoqKioKCiMjIyMgVGF4b25vbWljIGNvbXBvc2l0aW9uOiBDbGFzcy1sZXZlbCByZWxhdGl2ZSBhYnVuZGFuY2UKCmBgYHtyIHJlbF9hYnVuZF90YWJsZSwgZmlnLmFsaWduID0gJ2NlbnRlcid9CmRhdGF0YWJsZSh0YXhhX21lYW5zLCByb3duYW1lcyA9IEZBTFNFLCB3aWR0aCA9ICI2NSUiLCBjb2xuYW1lcyA9IGMoIkNsYXNzIiwgCiAgICAibWVhbiIpLCAKICAgIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbihzdHlsZSA9IAogICAgICAgICJjYXB0aW9uLXNpZGU6IGJvdHRvbTsgdGV4dC1hbGlnbjogbGVmdDsiLCAiVGFibGUgMjogIiwgCiAgICAgICAgaHRtbHRvb2xzOjplbSgiQ2xhc3MtbGV2ZWwgcmVsYXRpdmUgYWJ1bmRhbmNlLiIpKSwgCiAgICBleHRlbnNpb25zID0gIkJ1dHRvbnMiLCBvcHRpb25zID0gbGlzdChjb2x1bW5EZWZzID0gCiAgICAgICAgbGlzdChsaXN0KGNsYXNzTmFtZSA9ICJkdC1jZW50ZXIiLCB0YXJnZXRzID0gIl9hbGwiKSksIAogICAgICAgICBkb20gPSAiQmxmcnRpcCIsIHBhZ2VMZW5ndGggPSA1LCBsZW5ndGhNZW51ID0gYyg1LCAxMCwgNTAsIDcwKSwgCiAgICAgICAgIGJ1dHRvbnMgPSBjKCJjc3YiLCAiY29weSIpKSkKCndyaXRlLnRhYmxlKHRheGFfbWVhbnMsICJSX09VVFBVVC9jbGFzc19yZWxfYWJ1bmQudHh0Iiwgc2VwID0gIlx0Iiwgcm93Lm5hbWVzID0gRkFMU0UsIAogICAgcXVvdGUgPSBGQUxTRSkKYGBgCgpJbnNwZWN0aW5nIHRoZSB0YWJsZSBpdCBsb29rcyBsaWtlIGlmIHdlIGNob29zZSBhIGN1dG9mZiBvZiAyJSAoMC4wMikgd2UgZ2V0IDkgdGF4YS0tLXNvdW5kcyBwcmV0dHkgZ29vZC4gVGhlIHJlc3QgZ28gaW50byB0aGUgJ090aGVyJyBjYXRlZ29yeS4gTm8gbWF0dGVyIHdoYXQsIHdlIHdpbGwgYWx3YXlzIGdsb3NzIG92ZXIgc29tZSBncm91cHMgdXNpbmcgc3VjaCBhIGNvYXJzZSBhcHByb2FjaC4gQnV0IGFzIHdlIHdpbGwgc2VlIGxhdGVyLCBzb21lIG9mIHRoZXNlIGxvdyBhYnVuZGFuY2UgZ3JvdXBzIHdpbGwgcmVhcHBlYXIgd2hlbiB3ZSBsb29rIGF0IHRoZSBsZXZlbCBvZiBpbmRpdmlkdWFsIEFTVnMuIAoKSGVyZSB3ZSBkZWZpbmUgdGhlICoqT3RoZXIqKiBjYXRlZ29yeS4gCgoqKioKCmBgYHtyIGRlZmluZV9vdGhlcn0KIyBIZXJlIHdlIGNvbmdsb21lcmF0ZSBhdCAyJS4KT3RoZXIgPC0gbWVhbnNbbWVhbnMkbWVhbiA8PSAwLjAyLCBdJENsYXNzICAKIyBvciB5b3UgY2FuIGNob3NlIHNwZWNpZmMgdGF4YSBsaWtlIHRoaXMKI090aGVyX21hbnVhbCA8LSBjKCJsaXN0IiwgInRheGEiLCAiaW4iLCAidGhpcyIsICJmb3JtYXQiKQpsZW5ndGgoT3RoZXIpCmBgYAoKQXQgYSAyJSBhYnVuZGFuY2UgY3V0b2ZmLCA2MyBDbGFzc2VzIGFyZSBncm91cGVkIGludG8gdGhlICdPdGhlcicgY2F0ZWdvcnkuIEdyZWF0LCBub3cgd2UgY2FuIGNyYWZ0IHRoZSBiYXIgY2hhcnQuIEFuZCBoZXJlIGFyZSB0aGUgdGF4YSB0aGF0IHdpbGwgZ28gaW50byB0aGUgY2hhcnQ6CgpgYGB7ciBtZXRsZF9iYXJ9Cm1lbHRkW21lbHRkJENsYXNzICVpbiUgT3RoZXIsIF0kQ2xhc3MgPC0gIk90aGVyIgpzYW1wX25hbWVzIDwtIGFnZ3JlZ2F0ZShtZWx0ZCRBYnVuZGFuY2UsIGJ5ID0gbGlzdChtZWx0ZCRTYW1wbGUpLCBGVU4gPSBzdW0pWywgMV0KLmUgPC0gZW52aXJvbm1lbnQoKQptZWx0ZFssICJDbGFzcyJdIDwtIGZhY3RvcihtZWx0ZFssICJDbGFzcyJdLCBzb3J0KHVuaXF1ZShtZWx0ZFssICJDbGFzcyJdKSkpCm1lbHRkIDwtIG1lbHRkW29yZGVyKG1lbHRkWywgIkNsYXNzIl0pLCBdCiMgSGVyZSB3ZSBvcmRlciBDbGFzc2VzIGJ5IHRoZSBQaHlsdW0gdGhleSBiZWxvbmcgdG8uCmxldmVscyhtZWx0ZCRDbGFzcykKbWVsdGQkQ2xhc3MgPC0gZmFjdG9yKG1lbHRkJENsYXNzLCBsZXZlbHMgPSBjKCJCYWN0ZXJvaWRpYSIsICJDbG9zdHJpZGlhIiwgCiAgICAiRXJ5c2lwZWxvdHJpY2hpYSIsICJGdXNvYmFjdGVyaWlhIiwgIkFscGhhcHJvdGVvYmFjdGVyaWEiLCAiRGVsdGFwcm90ZW9iYWN0ZXJpYSIsIAogICAgIkdhbW1hcHJvdGVvYmFjdGVyaWEiLCAiUGxhbmN0b215Y2V0YWNpYSIsICJPeHlwaG90b2JhY3RlcmlhIiwgIk90aGVyIikpICAKYGBgCgpJdCB0b29rIHNvbWUgdHdlYWtpbmcgdG8gZ2V0IHRoZSBiYXIgY2hhcnQgdG8gbG9vayBqdXN0IHJpZ2h0LS0tc28gdGhlcmUgaXMgYSBsb3Qgb2YgY29kZSBoZXJlLS0tYW5kIGl0IGNvdWxkIG1vc3QgY2VydGFpbmx5IGJlIGJldHRlci4gV2hpbGUgd2UncmUgYXQgaXQsIHdlIHdpbGwgYWxzbyBzYXZlIGEgY29weSBvZiB0aGUgZmlndXJlIHNvIHdlIGNhbiB0d2VhayBpdCBsYXRlciBhbmQgbWFrZSBpdCBsb29rIHByZXR0eS4KCioqKgoKIyMjIyBUYXhvbm9taWMgY29tcG9zaXRpb246IENsYXNzIGFidW5kYW5jZSBhY3Jvc3MgaG9zdCBzcGVjaWVzICAKCiMjIyM8Zm9udCBjb2xvcj0icmVkIj5GaWd1cmUgMkE8L2ZvbnQ+CgpgYGB7ciBwbG90X2Jhcl9maWcyQSwgZmlnLmFsaWduID0gImNlbnRlciIsIGZpZy5jYXAgPSAiRmlndXJlIDJBIiwgZmlnLmhlaWdodCA9IDN9CmZpZzJBIDwtIGdncGxvdChtZWx0ZCwgYWVzX3N0cmluZyh4ID0gIlNhbXBsZSIsIHkgPSAiQWJ1bmRhbmNlIiwgZmlsbCA9ICJDbGFzcyIpLCAKICAgIGVudmlyb25tZW50ID0gLmUsIG9yZGVyZWQgPSBUUlVFLCB4bGFiID0gIngtYXhpcyBsYWJlbCIsIHlsYWIgPSAieS1heGlzIGxhYmVsIikgCmZpZzJBIDwtIGZpZzJBICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2socmV2ZXJzZSA9IFRSVUUpLCAKICAgIHdpZHRoID0gMC45NSkgKyBjb29yZF9mbGlwKCkgKyB0aGVtZShhc3BlY3QucmF0aW8gPSAxLzIpCmZpZzJBIDwtIGZpZzJBICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZnJpZW5kX3BhbCkKZmlnMkEgPC0gZmlnMkEgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMC40NSwgCiAgICB2anVzdCA9IDEpKQpmaWcyQSA8LSBmaWcyQSArIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoY29sb3VyID0gTlVMTCksIAogICAgcmV2ZXJzZSA9IEZBTFNFKSkgKyB0aGVtZShsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIpKQpmaWcyQSA8LSBmaWcyQSArIGxhYnMoeCA9ICJIb3N0IHNwZWNpZXMiLCB5ID0gIlJlbGF0aXZlIGFidW5kYW5jZSAoJSB0b3RhbCByZWFkcykiLCAKICAgIHRpdGxlID0gIkFidW5kYW5jZSBvZiBiYWN0ZXJpYWwgdGF4YSBhY3Jvc3MgaG9zdCBzcGVjaWVzIikKZmlnMkEgPC0gZmlnMkEgKyB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIAogICAgICAgIGZpbGwgPSBOQSwgc2l6ZSA9IDEpKQpmaWcyQQpwZGYoIlJfT1VUUFVUL0ZpZ3VyZV8yQS5wZGYiKQpmaWcyQQppbnZpc2libGUoZGV2Lm9mZigpKQpgYGAKCioqKgoKIyMjIyAqKkNvbW11bml0eSBEaXZlcnNpdHkqKgpBcm1lZCB3aXRoIGEgcGljdHVyZSBvZiB0YXhvbm9taWMgY29tcG9zaXRpb24gd2UgY2FuIG1vdmUgb24gdG8gZGl2ZXJzaXR5IGVzdGltYXRlcy4KCj4gV2UgdG9vayB0d28gdmlld3Mgb2Ygb3ZlcmFsbCBjb21tdW5pdHkgZGl2ZXJzaXR5LiBKdXN0IGNsaWNrIG9uIGEgdGFiIHRvIHNlZSB0aGUgY29kZSBhbmQgcmVzdWx0cy4gCgo8YSBpZD0iYmFjayB0byBkaXZlcnNpdHkgdGFicyI+PC9hPgoKIyMjICRcYWxwaGEkLWRpdmVyc2l0eSBzdGF0cwoKCkFscGhhIGRpdmVyc2l0eSBkZXNjcmliZXMgdGhlIGRpdmVyc2l0eSBpbiBhIHNhbXBsZSBvciBzaXRlLiAgVGhlcmUgYXJlIHNldmVyYWwgYWxwaGEgZGl2ZXJzaXR5IG1ldHJpY3MgYXZhaWxhYmxlIGluIHBoeWxvc2VxOiBgT2JzZXJ2ZWRgLCBgQ2hhbzFgLCBgQUNFYCwgYFNoYW5ub25gLCBgU2ltcHNvbmAsIGBJbnZTaW1wc29uYCwgYEZpc2hlcmAuIFBsYXkgYXJvdW5kIHRvIHNlZSBob3cgZGlmZmVyZW50IG1ldHJpY3MgY2hhbmdlIG9yIGNvbmZpcm0gdGhlc2UgcmVzdWx0cy4gCgpIZXJlIHdlIHdhbnQgdG8ga25vdyBpZiBkaXZlcnNpdHkgaXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgYWNyb3NzIGhvc3Qgc3BlY2llcy4gSW4gb3JkZXIgdG8gZG8gdGhhdCB3ZSBuZWVkIHRvIGtub3cgaWYgd2Ugc2hvdWxkIHJ1biBhIHBhcmFtZXRyaWMgb3Igbm9uLXBhcmFtZXRyaWMgdGVzdCwgYW5kIGZvciB0aGF0IHdlIG5lZWQgdG8ga25vdyBpZiBvdXIgZGF0YSBpcyBub3JtYWxseSBkaXN0cmlidXRlZC4gTW9zdCBvZiB0aGUgaWRlYXMvY29kZSBmb3IgYWxwaGEgKGFuZCBzdWJzZXF1ZW50IGJldGEpIGRpdmVyc2l0eSBzdGF0aXN0aWNzIGNvbWUgZnJvbSB0aGlzIFt3b3Jrc2hvcCB0dXRvcmlhbF0oaHR0cHM6Ly9ycHVicy5jb20vbWFkZGllU0MvUl9TT1BfVUNSX0phbl8yMDE4KXt0YXJnZXQ9Il9ibGFuayJ9IGJ5IEtpbSBEaWxsLU1jRmFybGFuZCBhbmQgTWFkaXNvbiBDb3guCgpGaXJzdCB3ZSBydW4gdGhlIGRpdmVyc2l0eSBlc3RpbWF0ZXMsIGFkZCB0aGVzZSBkYXRhIHRvIG91ciBzdW1tYXJ5IHRhYmxlLCBhbmQgc2F2ZSBhIGNvcHkgb2YgdGhpcyB0YWJsZS4KCiMjIyM8Zm9udCBjb2xvcj0icmVkIj5TdXBwbGVtZW50YXJ5IFRhYmxlIDM8L2ZvbnQ+CgpgYGB7ciBnZW5fc3VtbWFyeV90YWJsZSwgd2FybmluZyA9IEZBTFNFfQpkaXZlcnNpdHkgPC0gZXN0aW1hdGVfcmljaG5lc3MocHNfc2x2X3dvcmtfZmlsdCwgbWVhc3VyZXM9YygKICAgICAgICAgICAgIk9ic2VydmVkIiwgIkNoYW8xIiwgIkFDRSIsICJTaGFubm9uIiwgIlNpbXBzb24iLCAiSW52U2ltcHNvbiIsICJGaXNoZXIiKSkKZGl2ZXJzaXR5X2NhbGMgPC0gZGl2ZXJzaXR5ICU+JSByb3duYW1lc190b19jb2x1bW4oImhvc3RfSUQiKQojIHJvdW5kIHZhbHVlcwpkaXZlcnNpdHlfY2FsY1tjKDMsNSwxMCldIDwtIHJvdW5kKGRpdmVyc2l0eV9jYWxjW2MoMyw1LDEwKV0sIDEpCmRpdmVyc2l0eV9jYWxjW2MoNCw2LDcsOSldIDwtIHJvdW5kKGRpdmVyc2l0eV9jYWxjW2MoNCw2LDcsOSldLCAyKQpkaXZlcnNpdHlfY2FsY1s4XSA8LSByb3VuZChkaXZlcnNpdHlfY2FsY1s4XSwgMykKCmhvc3Rfc3VtbWFyeSA8LSBtZXJnZShob3N0X2RldGFpbHMsIGRpdmVyc2l0eV9jYWxjKQpob3N0X3N1bW1hcnkkT2JzZXJ2ZWQgPC0gTlVMTAoKd3JpdGUudGFibGUoaG9zdF9zdW1tYXJ5LCAiUl9PVVRQVVQvU3VwcGxlbWVudGFyeV9UYWJsZV8zLnR4dCIsIAogICAgICAgICAgICBzZXAgPSAiXHQiLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKYGBgCgpOZXh0LCB3ZSBhZGQgdGhlIGRpdmVyc2l0eSBlc3RpbWF0ZXMgdG8gb3VyIHBoeWxvc2VxIG9iamVjdCwgYW5kIHRlc3QgaWYgdGhlIGRhdGEgYXJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHVzaW5nIFNoYXBpcm8tV2lsayBOb3JtYWxpdHkgdGVzdC4gV2Ugd2lsbCBmb2N1cyBvbiB0aGUgaW52ZXJzZSBTaW1wc29uIGFuZCBTaGFubm9uIGRpdmVyc2l0eSBlc3RpbWF0ZXMgYW5kIENoYW/igJlzIHJpY2huZXNzIGVzdGltYXRlICBidXQgdGhpcyBhcHByb2FjaCBjYW4gYmUgdXNlZCBmb3IgYW55IG1ldHJpYy4KCmBgYHtyIGFscGhhX2Rpdl90ZXN0X25vcm0sIHdhcm5pbmcgPSBGQUxTRX0KIyBDb252ZXJ0IHRvIHBzIG9iamVjdApzYW1wbGVfZGl2IDwtIHNhbXBsZV9kYXRhKGRpdmVyc2l0eSkgCiMgQ3JlYXRlIG5ldyBwcyBvYmplY3Qgd2l0aCBkaXZlcnNpdHkgZXN0aW1hdGVzIGFkZGVkIHRvIHNhbXBsZV9kYXRhCnBzX3Nsdl93b3JrX2ZpbHRfZGl2IDwtIG1lcmdlX3BoeWxvc2VxKHBzX3Nsdl93b3JrX2ZpbHQsIHNhbXBsZV9kaXYpCiMgUnVuIFNoYXBpcm8gdGVzdApzaGFwaXJvX3Rlc3RfU2hhbiA8LSBzaGFwaXJvLnRlc3Qoc2FtcGxlX2RhdGEocHNfc2x2X3dvcmtfZmlsdF9kaXYpJFNoYW5ub24pCnNoYXBpcm9fdGVzdF9pbnZTaW1wIDwtIHNoYXBpcm8udGVzdChzYW1wbGVfZGF0YShwc19zbHZfd29ya19maWx0X2RpdikkSW52U2ltcHNvbikKc2hhcGlyb190ZXN0X0NoYW8xIDwtIHNoYXBpcm8udGVzdChzYW1wbGVfZGF0YShwc19zbHZfd29ya19maWx0X2RpdikkQ2hhbzEpCnNoYXBpcm9fdGVzdF9PYnNlcnZlZCA8LSBzaGFwaXJvLnRlc3Qoc2FtcGxlX2RhdGEocHNfc2x2X3dvcmtfZmlsdF9kaXYpJE9ic2VydmVkKQpgYGAKClNoYXBpcm8tV2lsayBOb3JtYWxpdHkgVGVzdCBmb3IgKipTaGFubm9uKiogaW5kZXguCmBgYHtyIHNoYXBfU2hhbiwgZWNobyA9IEZBTFNFfQpzaGFwaXJvX3Rlc3RfU2hhbgpgYGAKClNoYXBpcm8tV2lsayBOb3JtYWxpdHkgVGVzdCBmb3IgKippbnZlcnNlIFNpbXBzb24qKiBpbmRleC4KYGBge3Igc2hhcF9pbnZTLCBlY2hvID0gRkFMU0V9CnNoYXBpcm9fdGVzdF9pbnZTaW1wCmBgYAoKU2hhcGlyby1XaWxrIE5vcm1hbGl0eSBUZXN0IGZvciAqKkNoYW8xIHJpY2huZXNzKiogZXN0aW1hdG9yLgpgYGB7ciBzaGFwX0Nob2ExLCBlY2hvID0gRkFMU0V9CnNoYXBpcm9fdGVzdF9DaGFvMQpgYGAKClNoYXBpcm8tV2lsayBOb3JtYWxpdHkgVGVzdCBmb3IgKipPYnNlcnZlZCBBU1YgcmljaG5lc3MqKiBlc3RpbWF0b3IuCmBgYHtyIHNoYXBfT2JzZXJ2ZWQsIGVjaG8gPSBGQUxTRX0Kc2hhcGlyb190ZXN0X09ic2VydmVkCmBgYAoKT2ssIHNpbmNlIHRoZSBwLXZhbHVlcyBhcmUgc2lnbmlmaWNhbnQgZm9yIHRoZSBpbnZlcnNlIFNpbXBzb24sIENoYW8gcmljaG5lc3MsIGFuZCBPYnNlcnZlZCBBU1YgcmljaG5lc3Mgd2UgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGVzZSBkYXRhIGFyZSBub3JtYWxseSBkaXN0cmlidXRlZC4gSG93ZXZlciwgdGhlIFNoYW5ub24gZXN0aW1hdGVzIGFwcGVhciBub3JtYWxseSBkaXN0cmlidXRlZC4gU28gbGV0cyBzZWUgaWYgZGl2ZXJzaXR5IGlzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGJldHdlZW4gaG9zdCBzcGVjaWVzIGJhc2VkIG9uIHRoZSBTaGFubm9uIGluZGV4LiAKCiMjIyMgTm9ybWFsbHkgZGlzdHJpYnV0ZWQgbWV0cmljcwoKU2luY2UgdGhlIFNoYW5ub24gZGF0YSBpcyBub3JtYWxseSBkaXN0cmlidXRlZCB3ZSBjYW4gdGVzdCBmb3Igc2lnbmlmaWNhbmNlIHVzaW5nIEFOT1ZBIChhIHBhcmFtZXRyaWMgdGVzdCkuCgpgYGB7ciBub3JtYWx9CnNhbXBsZWRhdGFERiA8LSBkYXRhLmZyYW1lKHNhbXBsZV9kYXRhKHBzX3Nsdl93b3JrX2ZpbHRfZGl2KSkKYW92LnNoYW5ub24gPSBhb3YoU2hhbm5vbiB+IFNwLCBkYXRhID0gc2FtcGxlZGF0YURGKQojQ2FsbCBmb3IgdGhlIHN1bW1hcnkgb2YgdGhhdCBBTk9WQSwgd2hpY2ggd2lsbCBpbmNsdWRlIFAtdmFsdWVzCnN1bW1hcnkoYW92LnNoYW5ub24pCmBgYAoKT2ssIHRoZSByZXN1bHRzIG9mIHRoZSBBTk9WQSBhcmUgc2lnbmlmaWNhbnQuICBIZXJlIHdlIHVzZSB0aGUgVHVrZXkncyBIU0QgKGhvbmVzdGx5IHNpZ25pZmljYW50IGRpZmZlcmVuY2UpIHBvc3QtaG9jIHRlc3QgdG8gZGV0ZXJtaW5lIHdoaWNoIHBhaXJ3aXNlIGNvbXBhcmlzb25zIGFyZSBkaWZmZXJlbnQuCmBgYHtyIHR1a2V5fQpUdWtleUhTRChhb3Yuc2hhbm5vbikKYGBgCgpMb29rcyBsaWtlICpTcGFyaXNvbWEgYXVyb2ZyZW5hdHVtKiBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tICpTY2FydXMgdGFlbmlvcHRlcnVzKiBhbmQgKkFjYW50aHVydXMgdHJhY3R1cyouICAKCiMjIyMgTm9uLW5vcm1hbGx5IGRpc3RyaWJ1dGVkIG1ldHJpY3MKCk5vdyB3ZSBjYW4gbG9vayBhdCB0aGUgcmVzdWx0cyBvbiB0aGUgIGludmVyc2UgU2ltcHNvbiBkaXZlcnNpdHkgYW5kIENoYW/igJlzIHJpY2huZXNzLiBTaW5jZSBob3N0IHNwZWNpZXMgaXMgY2F0ZWdvcmljYWwsIHdlIHVzZSBLcnVza2FsLVdhbGxpcyAobm9uLXBhcmFtZXRyaWMgZXF1aXZhbGVudCBvZiBBTk9WQSkgdG8gdGVzdCBmb3Igc2lnbmlmaWNhbmNlLgoKKioqCgpLcnVza2FsLVdhbGxpcyBvZiAqKmludmVyc2UgU2ltcHNvbioqIGluZGV4LgoKYGBge3Iga3J1c2tfaW52c2ltcH0KI2xpYnJhcnkoRlNBKQojZHVublRlc3QoSW52U2ltcHNvbiB+IFNwLCBkYXRhID0gc2FtcGxlZGF0YURGLCBtZXRob2Q9ImJoIikgCmtydXNrYWwudGVzdChJbnZTaW1wc29uIH4gU3AsIGRhdGEgPSBzYW1wbGVkYXRhREYpCmBgYAoKS3J1c2thbC1XYWxsaXMgb2YgKipDaGFvMSByaWNobmVzcyoqIGVzdGltYXRvci4KCmBgYHtyIGtydXNrX2NoYW99CiNkdW5uVGVzdChDaGFvMSB+IFNwLCBkYXRhID0gc2FtcGxlZGF0YURGLCBtZXRob2Q9ImJoIikgCmtydXNrYWwudGVzdChDaGFvMSB+IFNwLCBkYXRhID0gc2FtcGxlZGF0YURGKQpgYGAKCktydXNrYWwtV2FsbGlzIG9mICoqT2JzZXJ2ZWQgQVNWIHJpY2huZXNzKiogaW5kZXguCgpgYGB7ciBrcnVza19vYnNlcnZlZH0KI2xpYnJhcnkoRlNBKQojZHVublRlc3QoT2JzZXJ2ZWQgfiBTcCwgZGF0YSA9IHNhbXBsZWRhdGFERiwgbWV0aG9kPSJiaCIpIAprcnVza2FsLnRlc3QoT2JzZXJ2ZWQgfiBTcCwgZGF0YSA9IHNhbXBsZWRhdGFERikKYGBgCgoqKioKCkZvciB0aGUgaW52ZXJzZSBTaW1wc29uLCBDaGFvMSwgYW5kIE9ic2VydmVkIHJpY2huZXNzIHRoZSByZXN1bHRzIG9mIHRoZSBLcnVza2FsLVdhbGxpcyByYW5rIHN1bSB0ZXN0IGFyZSBzaWduaWZpY2FudC4gU28gd2UgY2FuIGxvb2sgYXQgcGFpcndpc2UgY29tcGFyaXNvbnMgdXNpbmcgV2lsY294b24gcmFuayBzdW0gdGVzdCBmb3IgcG9zdC1ob2MgYW5hbHlzaXMuCgpQYWlyd2lzZSBzaWduaWZpY2FuY2UgdGVzdCBmb3IgKippbnZlcnNlIFNpbXBzb24qKiBpbmRleC4KCmBgYHtyIHdpbGNveF9pbnZzaW1wfQpwYWlyd2lzZS53aWxjb3gudGVzdChzYW1wbGVkYXRhREYkSW52U2ltcHNvbiwgc2FtcGxlZGF0YURGJFNwLCBwLmFkanVzdC5tZXRob2Q9ImZkciIpCmBgYAoKUGFpcndpc2Ugc2lnbmlmaWNhbmNlIHRlc3QgZm9yICoqQ2hhbzEgcmljaG5lc3MqKiBlc3RpbWF0b3IuCmBgYHtyIHdpbGNveF9jaGFvfQpwYWlyd2lzZS53aWxjb3gudGVzdChzYW1wbGVkYXRhREYkQ2hhbzEsIHNhbXBsZWRhdGFERiRTcCwgcC5hZGp1c3QubWV0aG9kPSJmZHIiKQpgYGAKClBhaXJ3aXNlIHNpZ25pZmljYW5jZSB0ZXN0IGZvciAqKk9ic2VydmVkIEFTViByaWNobmVzcyoqIGluZGV4LgoKYGBge3Igd2lsY294X29ic2VydmVkLCB3YXJuaW5nID0gRkFMU0V9CnBhaXJ3aXNlLndpbGNveC50ZXN0KHNhbXBsZWRhdGFERiRPYnNlcnZlZCwgc2FtcGxlZGF0YURGJFNwLCBwLmFkanVzdC5tZXRob2Q9ImZkciIpCmBgYAoKQWdhaW4gd2Ugc2VlIHRoYXQgb25seSAqU3AuIGF1cm9mcmVuYXR1bSogaXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSB0aGUgb3RoZXIgaG9zdHMuIEZvciB0aGUgaW52ZXJzZSBTaW1wc29uIGluZGV4LCAqU3AuIGF1cm9mcmVuYXR1bSogaXMgIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gdGhyZWUgb2YgdGhlIGZvdXIgaG9zdCBzcGVjaWVzIGFuZCBDaGFvMSByaWNobmVzcyBlc3RpbWF0b3IsICpTcC4gYXVyb2ZyZW5hdHVtKiBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIGFsbCBvdGhlciBob3N0IHNwZWNpZXMuIE5vdyB3ZSBjYW4gcGxvdCB0aGUgcmVzdWx0cy4gQ2xpY2sgb24gdGhlIFs8c3BhbiBzdHlsZT0iZm9udC1zaXplOjEuM2VtOyI+YmFjayB0byBkaXZlcnNpdHkgdGFiczwvc3Bhbj5dKCNiYWNrIHRvIGRpdmVyc2l0eSB0YWJzKSBsaW5rIHRvIGdvIHRvIHRoZSBhbHBoYSBkaXZlcnNpdHkgcGxvdHMuIAoKKioqCgojIyMgJFxhbHBoYSQtZGl2ZXJzaXR5IHBsb3RzCgpIZXJlIHdlIHBsb3QgdGhlIHJlc3VsdHMgb2YgU2hhbm5vbiBkaXZlcnNpdHkgaW5kZXguIFdlIHdpbGwgc2F2ZSBhIGNvcHkgb2YgdGhlIGZpZ3VyZSBmb3IgbGF0ZXIgdHdlYWtpbmcuIFdlIHVzZSB0aGUgY29sb3IgcGFsZXR0ZSBkZXNjcmliZWQgYWJvdmUgdG8gZGVsaW5lYXRlIGhvc3Qgc3BlY2llcy4KCgojIyMjPGZvbnQgY29sb3I9InJlZCI+RmlndXJlIDJCPC9mb250PgoKYGBge3IgYWxwaGFfZGl2X2ZpZ18yQiwgZmlnLmFsaWduID0gImNlbnRlciIsIGZpZy5jYXAgPSAiRmlndXJlIDJCIiwgd2FybmluZyA9IEZBTFNFfQpmaWcyQiA8LSBwbG90X3JpY2huZXNzKHBzX3Nsdl93b3JrX2ZpbHQsIHggPSAiU3AiLCAKICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlcyA9IGMoIlNoYW5ub24iKSwgCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiU3AiLCBucm93ID0gMSkKZmlnMkIgPC0gZmlnMkIgKyBnZW9tX2JveHBsb3QoKSArIGdlb21faml0dGVyKHdpZHRoID0gMC4wNSkKZmlnMkIgPC0gZmlnMkIgKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHNhbXBfcGFsKSArIAogICAgICAgICBsYWJzKHggPSAiSG9zdCBzcGVjaWVzIiwgCiAgICAgICAgIHkgPSAiRGl2ZXJzaXR5IiwgCiAgICAgICAgIHRpdGxlID0gIkFscGhhIGRpdmVyc2l0eSBvZiBiYWN0ZXJpYWwgCiAgICAgICAgIGNvbW11bml0aWVzIGluIGhlcmJpdm9yb3VzIHJlZWYgZmlzaCIpCiNmaWcyQiArIGdlb21fYm94cGxvdChhZXMoY29sb3VyID0gYmxhY2spKQojZmlnMkIgPC0gZmlnMkIgKyB0aGVtZV9idygpICsgZ2VvbV9wb2ludChzaXplID0gMi41LCBhZXMoY29sb3IgPSBTcCkpICsgCmZpZzJCCnBkZigiUl9PVVRQVVQvRmlndXJlXzJCLnBkZiIpCmZpZzJCCmludmlzaWJsZShpbnZpc2libGUoZGV2Lm9mZigpKSkKYGBgCgpDbGljayBvbiB0aGUgWzxzcGFuIHN0eWxlPSJmb250LXNpemU6MS4zZW07Ij5iYWNrIHRvIGRpdmVyc2l0eSB0YWJzPC9zcGFuPl0oI2JhY2sgdG8gZGl2ZXJzaXR5IHRhYnMpIHRvIGV4cGxvcmUgY29ycmVsYXRpb25zIHdpdGggYWxwaGEgZGl2ZXJzaXR5LgoKKioqCgojIyMgJFxiZXRhJC1kaXZlcnNpdHkgcGxvdHMKCkJldGEgZGl2ZXJzaXR5IGJhc2ljYWxseSB0ZWxscyB1cyBob3cgc2ltaWxhciBvciBkaXNzaW1pbGFyIHNhbXBsZXMgYXJlIHRvIG9uZSBhbm90aGVyLiBQaHlsb3NlcSBvZmZlcnMgc2V2ZXJhbCBvcmRpbmF0aW9uIGBtZXRob2RzYCBhbmQgYGRpc3RhbmNlYCBtZXRyaWNzLiBIZXJlIHdlIHVzZSBub24gbWV0cmljIG11bHRpZGltZW5zaW9uYWwgc2NhbGluZyAoTk1EUykgY291cGxlZCB3aXRoIEplbnNlbuKAk1NoYW5ub24gZGl2ZXJnZW5jZS4gV2UgYWxzbyBzYXZlIGEgY29weSBvZiB0aGUgZmlndXJlIGZvciBsYXRlciB0d2Vha2luZy4gVG8gc2VlIHRoZSBmdWxsIG91dHB1dCBvZiB0aGUgTk1EUyBhbmFseXNpcywgcmVtb3ZlIHRoZSBgcmVzdWx0cyA9ICdoaWRlJ2AgdGFnIGZyb20gdGhlIGNvZGUgY2h1bmsuCgpgYGB7ciBydW5fbm1kcywgcmVzdWx0cyA9ICdoaWRlJ30gCnNldC5zZWVkKDMxMzEpCm9yZC5ubWRzLmpzZF9zbHYgPC0gb3JkaW5hdGUocHNfc2x2X3dvcmtfZmlsdCwgbWV0aG9kID0gIk5NRFMiLCBkaXN0YW5jZSA9ICJqc2QiKQpzdHJlc3NwbG90KG9yZC5ubWRzLmpzZF9zbHYpCgpgYGAKCmBgYHtyIG9yZF9yZXN1bHRzLCBlY2hvID0gRkFMU0V9Cm9yZC5ubWRzLmpzZF9zbHYKYGBgCldlIHNlZSB0aGF0IGEgY29udmVyZ2VudCBzb2x1dGlvbiB3YXMgcmVhY2hlZCBhcm91bmQgMjAgaXRlcmF0aW9ucyBhbmQgb3VyIHN0cmVzcyBpcyBiZWxvdyAwLjIwLCBtZWFuaW5nIHRoYXQgMi1heGVzIGFyZSBzdWZmaWNpZW50IHRvIHZpZXcgdGhlIGRhdGEuIEdlbmVyYWxseSwgd2UgYXJlIGxvb2tpbmcgZm9yIHN0cmVzcyB2YWx1ZXMgYmVsb3cgMC4yLiBJZiB0aGUgc3RyZXNzIHZhbHVlcyBhcmUgaGlnaCwgeW91IG1heSBuZWVkIHRvIGFkZCBtb3JlIGF4ZXMgdG8gdGhlIG9yZGluYXRpb24uIExldHMgdmlzdWFsaXplIHRoZSBwbG90LgoKIyMjIzxmb250IGNvbG9yPSJyZWQiPkZpZ3VyZSAyQzwvZm9udD4KCmBgYHtyIGJldGFfZGl2X2ZpZ18yQywgZmlnLmFsaWduID0gImNlbnRlciIsIGZpZy5jYXAgPSAiRmlndXJlIDJDIn0KZmlnMkMgPC0gcGxvdF9vcmRpbmF0aW9uKHBzX3Nsdl93b3JrX2ZpbHQsIG9yZC5ubWRzLmpzZF9zbHYsIGNvbG9yID0gIlNwIiwgCiAgICBsYWJlbCA9ICJTYW1OYW1lIiwgdGl0bGUgPSAiSmVuc2VuLVNoYW5ub24gZGl2ZXJnZW5jZSIpCmZpZzJDIDwtIGZpZzJDICsgZ2VvbV9wb2ludChzaXplID0gNCkgKyBnZW9tX3BvaW50KHNoYXBlID0gMSwgCiAgICAgICAgICAgICAgc2l6ZSA9IDMuNiwgY29sb3VyID0gImJsYWNrIiwgc3Ryb2tlID0gMC43NSkgIyArICB4bGltKC0wLjQsIDAuNCkgKyB5bGltKC0wLjQsIDAuNCkKZmlnMkMgPC0gZmlnMkMgKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHNhbXBfcGFsKQpmaWcyQyA8LSBmaWcyQyArIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiYmxhY2siKSwgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKCJncmV5IiksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoImdyZXkiKSwgCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbCA9IE5BLCBzaXplID0gMSkpICsgCiAgICB0aGVtZShsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIpKQpmaWcyQyA8LSBmaWcyQyArIGNvb3JkX2ZpeGVkKCkKZmlnMkMgPC0gZmlnMkMgICsgc3RhdF9lbGxpcHNlKHR5cGUgPSAidCIpICsgdGhlbWVfYncoKQpmaWcyQwpwZGYoIlJfT1VUUFVUL0ZpZ3VyZV8yQy5wZGYiKQpmaWcyQwppbnZpc2libGUoZGV2Lm9mZigpKQpgYGAKClNvIHdlIGNhbiBzZWUgc29tZSBjbHVzdGVyaW5nIHdpdGhpbiBncm91cHMgYW5kIHNwcmVhZCBiZXR3ZWVuIGdyb3VwcywgYnV0IHRoaXMgaXMgbm90IGEgdGVzdCBmb3Igc3RhdGlzdGljYWwgZGlmZmVyZW5jZXMuIERvIG1pY3JvYmlhbCBjb21tdW5pdGllcyBkaWZmZXIgc2lnbmlmaWNhbnRseSBieSBob3N0IHRheGE/IENsaWNrIG9uIHRoZSBbPHNwYW4gc3R5bGU9ImZvbnQtc2l6ZToxLjNlbTsiPmJhY2sgdG8gZGl2ZXJzaXR5IHRhYnM8L3NwYW4+XSgjYmFjayB0byBkaXZlcnNpdHkgdGFicykgdG8gc2VlIGhvdyB3ZSBlbXBsb3kgc29tZSBzdGF0aXN0aWNhbCB0ZXN0cyBvZiBiZXRhLWRpdmVyc2l0eS4KCiMjIyAkXGJldGEkLWRpdmVyc2l0eSBzdGF0cwoKVG8gdGVzdCB3aGV0aGVyIG1pY3JvYmlhbCBjb21tdW5pdGllcyBkaWZmZXIgYnkgaG9zdCBzcGVjaWVzIHdlIGNhbiB1c2UgcGVybXV0YXRpb25hbCBhbmFseXNpcyBvZiB2YXJpYW5jZSAoUEVSTUFOT1ZBKSBvciBhbmFseXNpcyBvZiBzaW1pbGFyaXR5IChBTk9TSU0pLiBQRVJNQU5PVkEgZG9lcyBub3QgYXNzdW1lIG5vcm1hbGl0eSBidXQgZG9lcyBhc3N1bWUgZXF1YWwgYmV0YSBkaXNwZXJzaW9uIGJldHdlZW4gZ3JvdXBzLiBXZSB3aWxsIHRlc3QgYmV0YSBkaXNwZXJzaW9uIGJlbG93LgoKRmlyc3QgIHdlIHVzZSB0aGUgYGFkb25pc2AgZnVuY3Rpb24gaW4gdmVnYW4gdG8gcnVuIGEgUEVSTUFOT1ZBIHRlc3QuIFRoaXMgd2lsbCB0ZWxsIHVzIHdoZXRoZXIgaG9zdCBzcGVjaWVzIGhhdmUgc2ltaWxhciBjZW50cm9pZHMgb3Igbm90LiAKCmBgYHtyIG9yZGluYXRpb25fc3RhdHNfYWRvbmlzfQpzZXQuc2VlZCgxOTExKQpmaXNoLmpzZCA8LSBwaHlsb3NlcTo6ZGlzdGFuY2UocHNfc2x2X3dvcmtfZmlsdCwgbWV0aG9kID0gImpzZCIpCnNhbXBsZWRmIDwtIGRhdGEuZnJhbWUoc2FtcGxlX2RhdGEocHNfc2x2X3dvcmtfZmlsdCkpCmZpc2hfYWRvbmlzIDwtIGFkb25pcyhmaXNoLmpzZCB+IFNwLCBkYXRhID0gc2FtcGxlZGYsIHBlcm11dGF0aW9ucyA9IDEwMDApCmZpc2hfYWRvbmlzCmBgYAoKVGhlc2UgcmVzdWx0cyBpbmRpY2F0ZSB0aGF0IGNlbnRyb2lkcyBhcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgYWNyb3NzIGhvc3Qgc3BlY2llcyBtZWFuaW5nIHRoYXQgY29tbXVuaXRpZXMgYXJlIGRpZmZlcmVudCBieSBob3N0IHNwZWNpZXMuIAoKV2UgY2FuIGFsc28gdXNlIHRoZSBgcGFpcndpc2VBZG9uaXNgIHBhY2thZ2UgZm9yIHBhaXItd2lzZSBQRVJNQU5PVkEgYW5hbHlzaXMuCgpgYGB7ciBwYWlyd2lzZV9hZG9uaXN9CnBhaXJ3aXNlLmFkb25pcyhmaXNoLmpzZCwgZmFjdG9ycyA9IHNhbXBsZWRmJFNwLCBwLmFkanVzdC5tID0gImJvbmZlcnJvbmkiKQpgYGAKCkhlcmUgd2Ugc2VlICBhZ2FpbiB3ZSBzZWUgdGhhdCBjb21tdW5pdGllcyBhcmUgZGlmZmVyZW50IGJ5IGhvc3Qgc3BlY2llcy4gCgpIb3dldmVyLCBQRVJNQU5PVkEgYXNzdW1lcyBlcXVhbCBiZXRhIGRpc3BlcnNpb24gc28gd2Ugd2lsbCAgdXNlIHRoZSBgYmV0YWRpc3BlcmAgZnVuY3Rpb24gZnJvbSB0aGUgYHZlZ2FuYCBwYWNrYWdlIHRvIGNhbGN1bGF0ZSBiZXRhIGRpc3BlcnNpb24gdmFsdWVzLiAKCmBgYHtyIGJldGFkaXNwZXJ9CmJldGFfYWRvbmlzIDwtIGJldGFkaXNwZXIoZmlzaC5qc2QsIHNhbXBsZWRmJFNwLCBiaWFzLmFkanVzdCA9IFRSVUUpCmJldGFfYWRvbmlzCmBgYAoKQW5kIHRoZW4gYSBwYWlyLXdpc2UgUGVybXV0YXRpb24gdGVzdCBmb3IgaG9tb2dlbmVpdHkgb2YgbXVsdGl2YXJpYXRlIGRpc3BlcnNpb25zIHVzaW5nIGBwZXJtdXRlc3RgIChhZ2FpbiBmcm9tIHRoZSBgdmVnYW5gIHBhY2thZ2UpLiAKYGBge3IgcGVybXV0ZXN0fQpwZXJtdXRlc3QoYmV0YV9hZG9uaXMsIHBhaXJ3aXNlPVRSVUUsIHBlcm11dGF0aW9ucz0xMDAwKQpgYGAKClRoZXNlIHJlc3VsdHMgYXJlIHNpZ25pZmljYW50LCBtZWFuaW5nIHRoYXQgIGhvc3Qgc3BlY2llcyBoYXZlIGRpZmZlcmVudCBkaXNwZXJzaW9ucy4gTG9va2luZyBhdCB0aGUgcGFpcndpc2UgcC12YWx1ZXMgYW5kIHBlcm11dGVkIHAtdmFsdWUsIHdlIHNlZSB0aGF0IHRoZSAgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMgKHAtdmFsdWUgPCAwLjA1KSBhcmUgYmV0d2VlbjogCgoqIFNwQXVyICYgQWNDb2UsIEFjVHJhCiogU2NUYWUgJiBBY0NvZSwgQWNUcmEsIFNwVmlyCgpUaGlzIG1lYW5zIHdlIGFyZSBsZXNzIGNvbmZpZGVudCB0aGF0IHRoZSBQRVJNQU5PVkEgcmVzdWx0IGlzIGEgcmVhbCByZXN1bHQsIGFuZCB0aGF0IHRoZSByZXN1bHQgaXMgcG9zc2libHkgZHVlIHRvIGRpZmZlcmVuY2VzIGluIGdyb3VwIGRpc3BlcnNpb25zLgoKV2UgY2FuIGFsc28gdXNlIEFuYWx5c2lzIG9mIFNpbWlsYXJpdHkgKEFOT1NJTSktLS13aGljaCBkb2VzIG5vdCBhc3N1bWUgZXF1YWwgZ3JvdXAgdmFyaWFuY2VzLS0tdG8gdGVzdCB3aGV0aGVyIG92ZXJhbGwgbWljcm9iaWFsIGNvbW11bml0aWVzIGRpZmZlciBieSBob3N0IHNwZWNpZXMuCgpgYGB7ciBvcmRpbmF0aW9uX3N0YXRzX2Fub3NpbX0Kc3Bncm91cCA8LSBnZXRfdmFyaWFibGUocHNfc2x2X3dvcmtfZmlsdCwgIlNwIikKZmlzaF9hbm9zaW0gPC0gYW5vc2ltKGRpc3RhbmNlKHBzX3Nsdl93b3JrX2ZpbHQsICJqc2QiKSwgZ3JvdXBpbmcgPSBzcGdyb3VwKQpzdW1tYXJ5KGZpc2hfYW5vc2ltKQpgYGAKCkFuZCB0aGUgQU4wU0lNIHJlc3VsdCBpcyBzaWduaWZpY2FudCBtZWFuaW5nIHRoYXQgaG9zdCBzcGVjaWVzIGluZmx1ZW5jZXMgbWljcm9iaWFsIGNvbW11bml0eSBjb21wb3NpdGlvbi4gCgpbYmFjayB0byBkaXZlcnNpdHkgdGFic10oI2JhY2sgdG8gZGl2ZXJzaXR5IHRhYnMpCgoqKioKCjxhIGlkPSJQYXJ0IElWOiBEaWZmZXJlbnRpYWxseSBBYnVuZGFudCBBU1ZzIj48L2E+ICAKCiMjUGFydCBJVjogRGlmZmVyZW50aWFsbHkgQWJ1bmRhbnQgQVNWcyAKCltiYWNrIHRvIHRvcF0oI2JhY2sgdG8gdG9wKQoKQXQgdGhpcyBwb2ludCB3ZSBoYXZlIGEgZ29vZCBoYW5kbGUgb24gdGhlIGRpdmVyc2l0eSBvZiB0aGUgaW50ZXN0aW5hbCBtaWNyb2Jpb21lcyBvZiB0aGVzZSBoZXJiaXZvcm91cyByZWVmIGZpc2guIFdlIGtub3cgdGhhdCBjb21tdW5pdGllcyBhcmUgZG9taW5hdGVkIGJ5IHRoZSBzYW1lIGJyb2FkLWxldmVsIHRheG9ub21pYyBncm91cHMuIFRoZSBiZXRhIGRpdmVyc2l0eSBhbmFseXNpcyBkZW1vbnN0cmF0ZXMgdGhhdCBjb21tdW5pdGllcyBwYXJ0aXRpb24gYWxvbmcgaG9zdCBzcGVjaWVzLiBOb3cgd2Ugd2FudCB0byBkZXRlcm1pbmUgd2hpY2ggQVNWcyBhcmUgZHJpdmluZyB0aGVzZSBwYXR0ZXJucyBhbmQgYXNzZXNzIHRoZWlyIGRpc3RyaWJ1dGlvbiBpbiBuYXR1cmUgdXNpbmcgcHVibGljbHkgYXZhaWxhYmxlIGRhdGEuIFRvIGFjY29tcGxpc2ggdGhpcyB0YXNrIHdlIGxlYXZlIHRoZSBgUmAgZW52aXJvbm1lbnQgYW5kIGVtcGxveSBzb21lIGFkZGl0aW9uYWwgdG9vbHMuIFRvIHN1bW1hcml6ZSwgb3VyIGdvYWxzIGhlcmUgYXJlIHRvOgoKPiBBKSBpZGVudGlmeSBkaWZmZXJlbnRpYWxseSBhYnVuZGFudCAoREEpIEFTVnMgYWNyb3NzIGhvc3Qgc3BlY2llcywgCj4gQikgZmluZCBjbG9zZXN0IGRhdGFiYXNlIG1hdGNoZXMgdG8gREEgQVNWcywgYW5kIAo+IEMpIHBlcmZvcm0gcGh5bG9nZW5ldGljIHJlY29uc3RydWN0aW9uIG9uIERBIEFTVnMgYW5kIHRvcCBoaXRzLgoKCiMjIyBEaWZmZXJlbnRpYWwgYWJ1bmRhbmNlIChEQSkKV2UgdXNlZCBbTERBIEVmZmVjdCBTaXplIChMRWZTZSldKGh0dHBzOi8vZHguZG9pLm9yZy8xMC4xMDM4JTJGbm1ldGguMjY1OCl7dGFyZ2V0PSJfYmxhbmsifSB0byBpZGVudGlmeSBkaWZmZXJlbnRpYWxseSBhYnVuZGFudCAoREEpIEFTVnMgYWNyb3NzIGhvc3Qgc3BlY2llcyBhbmQgdGhlIFtNaWNyb2Jpb21lQW5hbHlzdCB3ZWJzZXJ2ZXJdKGh0dHA6Ly93d3cubWljcm9iaW9tZWFuYWx5c3QuY2EvKXt0YXJnZXQ9Il9ibGFuayJ9IHRvIHJ1biB0aGUgYW5hbHlzaXMuIFRoZXJlIGFyZSBhbHNvIG1hbnkgb3RoZXIgZ3JlYXQgdG9vbHMgb24gdGhlIE1pY3JvYmlvbWVBbmFseXN0IHdlYnNlcnZlciBieSB0aGUgd2F5LiBXZSBuZWVkZWQgdGhyZWUgZmlsZXMgZm9yIHRoZSBpbnB1dC0tLWFuICdPVFUnIHRhYmxlLCBhIG1ldGFkYXRhIGZpbGUgY29udGFpbmluZyBzYW1wbGUgaW5mb3JtYXRpb24sIGFuZCBhIHRheG9ub215IHRhYmxlLiBXZSBnZW5lcmF0ZSB0aGVzZSB0YWJsZXMgd2l0aCB0aGUgY29kZSBiZWxvdy4KCgpgYGB7ciBnZW5fbGVmc2VfaW5wdXRfZmlsZXMsIHJlc3VsdHM9J2hpZGUnfQojIyMjIyMjIyMjIE9UVSB0YWJsZQpPVFUxIDwtICBhcyhvdHVfdGFibGUocHNfc2x2X3dvcmtfZmlsdCksICJtYXRyaXgiKQojIHRyYW5zcG9zZSBpZiBuZWNlc3NhcnkKIyBDb2VyY2UgdG8gZGF0YS5mcmFtZQpPVFVkZiA8LSBhcy5kYXRhLmZyYW1lKHQoT1RVMSkpCnNldERUKE9UVWRmLCBrZWVwLnJvd25hbWVzID0gVFJVRSlbXQp3cml0ZS50YWJsZShPVFVkZiwgIlJfT1VUUFVUL3NlcV90YWJfZm9yX2NvcmUudHh0IiwgCiAgICBzZXAgPSAiXHQiLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKY29sbmFtZXMoT1RVZGYpWzFdIDwtICIjTkFNRSIKd3JpdGUudGFibGUoT1RVZGYsICJMRWZTZV9JTlBVVC9MRWZTZV9JTlBVVF9zZXFfdGFiLnR4dCIsIAogICAgc2VwID0gIlx0Iiwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCiMjIyMjIyMjIyMjIyBUQVggVGFibGUKIyBSZW1lbWJlciBpbiB0aGUgYHRheF90YWJsZWAgd2UgYWRkZWQgdGhlIGxhc3QgY29sdW1ucyBhcyB0aGUgYWN0dWFsIHNlcXVlbmNlIAojIG9mIGVhY2ggQVNWIGFuZCB0aGUgQVNWX0lELiBXZSBkbyBub3QgbmVlZCB0aG9zZSBoZXJlLgojIFNvIGxldHMgb25seSBrZWVwIHRoZSBmaXJzdCA2IGNvbHVtbnMgKHRoZSB0YXhvbm9taWMgbGluZWFnZSkKVEFYMSA8LSBhcyh0YXhfdGFibGUocHNfc2x2X3dvcmtfZmlsdCksICJtYXRyaXgiKQpUQVhkZiA8LSBhcy5kYXRhLmZyYW1lKFRBWDEpCnNldERUKFRBWGRmLCBrZWVwLnJvd25hbWVzID0gVFJVRSlbXQpjb2xuYW1lcyhUQVhkZilbMV0gPC0gIiNUQVhPTk9NWSIKVEFYZGYgPC0gVEFYZGZbLCAxOjZdCndyaXRlLnRhYmxlKFRBWGRmLCAiTEVmU2VfSU5QVVQvTEVmU2VfSU5QVVRfdGF4X3RhYi50eHQiLCAKICAgIHNlcCA9ICJcdCIsIHJvdy5uYW1lcyA9IEZBTFNFLCBxdW90ZSA9IEZBTFNFKQojIyMjIyMjIyMjIyMgTWV0YWRhdGEgZmlsZQptZXRhX2ZpbGUgPC0gZGF0YS5mcmFtZShOQU1FID0gc2FtcGxlX25hbWUsIFNhbXBsZVR5cGUgPSBzcGVjaWVzLCBHZW4gPSBnZW51cykKcm93bmFtZXMobWV0YV9maWxlKSA8LSBzYW1wbGVzLm91dApjb2xuYW1lcyhtZXRhX2ZpbGUpIDwtICBjKCIjTkFNRSIsICJTcGVjaWVzIiwgIkdlbnVzIiApCiMgYnV0IHdlIHN0aWxsIGhhdmUgdGhvc2UgdGhyZWUgc2FtcGxlcyB0aGF0IG5lZWQgdG8gYmUgcmVtb3ZlZAptZXRhX2ZpbGUgPC0gZmlsdGVyKG1ldGFfZmlsZSwgU3BlY2llcyAhPSAiU3BDaHIiICYgU3BlY2llcyAhPSAiU2NWZXQiKQp3cml0ZS50YWJsZShtZXRhX2ZpbGUsICJMRWZTZV9JTlBVVC9MRWZTZV9JTlBVVF9tZXRhZGF0YS50eHQiLCBzZXAgPSAiXHQiLCByb3cubmFtZXMgPSBGQUxTRSwgCiAgICBxdW90ZSA9IEZBTFNFKQpgYGAKCgpBbmQgb25jZSB3ZSBoYXZlIHRoZSB0aHJlZSBmaWxlcywgd2UgaGVhZCBvdmVyIHRvIHRoZSBbTWljcm9iaW9tZUFuYWx5c3Qgd2Vic2VydmVyXShodHRwOi8vd3d3Lm1pY3JvYmlvbWVhbmFseXN0LmNhLyl7dGFyZ2V0PSJfYmxhbmsifSBhbmQgdXBsb2FkIHRoZSBmaWxlcy4gQmUgc3VyZSB0byBzZWxlY3QgYFNpbHZhIHRheG9ub215YCBpbiB0aGUgZHJvcC1kb3duIG1lbnUuICAKQ2hlY2sgdGhlIGRhdGEgc3VtbWFyeSBhZnRlciB1cGxvYWRpbmcgdGhlIGZpbGVzOiAKCiogT1RVIGFubm90YXRpb246CVNJTFZBCiogT1RVIG51bWJlcjoJMTExNDQKKiBPVFUgd2l0aCDiiaUgMiBjb3VudHM6CTQxMjEKKiBTYW1wbGUgbnVtYmVyOgk1MAoqIE51bWJlciBvZiBleHBlcmltZW50YWwgZmFjdG9yczoJMgoqIFRvdGFsIHJlYWQgY291bnRzOgkyODI4MTEyCiogQXZlcmFnZSBjb3VudHMgcGVyIHNhbXBsZToJNTY1NjIKKiBNYXhpbXVtIGNvdW50cyBwZXIgc2FtcGxlOgkxNzUxMTYKKiBNaW5pbXVtIGNvdW50cyBwZXIgc2FtcGxlOgkxMTU2OAoKQ29vbCwgYWxsIGxvb2tzIGdvb2QuIEhpdCBgUHJvY2VlZGAuIEhlcmUgYXJlIHRoZSBzZXR0aW5ncyB3ZSB1c2VkIGZvciB0aGUgZGlmZmVyZW50IHN0ZXA6CgoqICoqRmlsdGVyIHRoZSBkYXRhKio6IGBNaW5pbXVtIGNvdW50ID0gMjBgLCBgUHJldmFsZW5jZSBpbiBzYW1wbGVzICglKSA9IDIwYCwgYW5kIGBQZXJjZW50YWdlIHRvIHJlbW92ZSAoJSkgPSAwYC4gVGhpcyByZW1vdmVkICoqMzc5NioqIGxvdyBhYnVuZGFudCBBU1ZzLiAgIAoqICoqRGF0YSBOb3JtYWxpemF0aW9uKio6IApgRGF0YSByYXJlZnlpbmcgPSBEbyBub3QgcmFyZWZ5IG15IGRhdGFgLCAgIApgRGF0YSBzY2FsaW5nID0gVG90YWwgc3VtIHNjYWxpbmcgKFRTUylgLCBhbmQgICAKYERhdGEgdHJhbnNmb3JtYXRpb24gPSBEbyBub3QgdHJhbnNmb3JtIG15IGRhdGFgLiAgIAoqICoqTEVmU2UgYW5hbHlzaXMqKiAgYExvZyBMREEgc2NvcmUgPSA0YCAmIGBBZGp1c3RlZCBwLXZhbHVlIGN1dG9mZiA9IDAuMDAwMWAuIFdlIHNwZWNpZmljYWxseSBjaG9zZSB0aGVzZSB2YWx1ZXMgYmVjYXVzZSB3ZSBmb3VuZCB0aGF0IHRoZXkgZWxpbWluYXRlZCBzcHVyaW91cyByZXN1bHRzIHN1Y2ggYXMgREEgQVNWcyB0aGF0IHdlcmUgcmVhbGx5IGFidW5kYW50IGluIGEgZmV3IHNhbXBsZXMgYnV0IG5vdCBjb25zaXN0ZW50IGFjcm9zcyBhbiBlbnRpcmUgZ3JvdXAuCgo+IFRoZSByZXN1bHQgd2FzICoqNTkqKiBkaWZmZXJlbnRpYWxseSBhYnVuZGFudCAoKipEQSoqKSBBU1ZzLiAgCgoqKioKCiMjIyMgUmVzdWx0cyBvZiBMRWZTZSBhbmFseXNpcwoKV2UgY2FuIGluc3BlY3QgYW5kIHNhdmUgdGhlIHJlc3VsdHMgb2YgdGhlIExFZlNlIGFuYWx5c2lzLiBUaGUgdGFibGUgc2hvd3MgdGhlIExpbmVhciBkaXNjcmltaW5hbnQgYW5hbHlzaXMgKExEQSkgc2NvcmVzLCBQLXZhbHVlcyBhZGp1c3RlZCBmb3IgbXVsdGlwbGUgdGVzdGluZywgYW5kIEZhbHNlIERpc2NvdmVyeSBSYXRlIChGRFIpIHZhbHVlcyBmcm9tIHRoZSBMRWZTZSBhbmFseXNpcy4gTm9ybWFsaXplZCByZWFkIGFidW5kYW5jZSB2YWx1ZXMgZm9yIGVhY2ggaG9zdCBzcGVjaWVzIGFyZSBhbHNvIGdpdmVuLgoKIyMjIzxmb250IGNvbG9yPSJyZWQiPlN1cHBsZW1lbnRhcnkgVGFibGUgNTwvZm9udD4KCmBgYHtyIGxlZnNlX3RhYmxlfQpsZWZzZV90YWIgPC0gcmVhZC50YWJsZSgiTUFOVUFMX0lOUFVUL2xlZnNlX3Jlc3VsdHMudHh0IiwgaGVhZGVyID0gVFJVRSwgCiAgICBzZXAgPSAiXHQiLCBjaGVjay5uYW1lcyA9IEZBTFNFKQp3cml0ZS50YWJsZShsZWZzZV90YWIsICJSX09VVFBVVC9TdXBwbGVtZW50YXJ5X1RhYmxlXzUudHh0IiwgcXVvdGUgPSBGQUxTRSwgc2VwID0gIlx0Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmRhdGF0YWJsZShsZWZzZV90YWIsIHJvd25hbWVzID0gRkFMU0UsIHdpZHRoID0gIjEwMCUiLCBjYXB0aW9uID0gCiAgICBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbihzdHlsZSA9ICJjYXB0aW9uLXNpZGU6IGJvdHRvbTsgdGV4dC1hbGlnbjogbGVmdDsiLCAKICAgICJUYWJsZSA0OiAiLCBodG1sdG9vbHM6OmVtKCJSZXN1bHRzIG9mIExFZlNlIGFuYWx5c2lzLiIpKSwgZXh0ZW5zaW9ucyA9ICJGaXhlZENvbHVtbnMiLCAKICAgICJCdXR0b25zIiwgb3B0aW9ucyA9IGxpc3QoY29sdW1uRGVmcyA9IGxpc3QobGlzdChjbGFzc05hbWUgPSAiZHQtY2VudGVyIiwgCiAgICAgICAgdGFyZ2V0cyA9IGMoMSwgMiwgMywgNCwgNSwgNiwgNywgOCkpKSwgZG9tID0gIkJsZnJ0aXAiLCBwYWdlTGVuZ3RoID0gNSwgCiAgICAgICAgbGVuZ3RoTWVudSA9IGMoNSwgMTAsIDI1LCA2MCksIGJ1dHRvbnMgPSBjKCJjc3YiLCAiY29weSIpLCBzY3JvbGxYID0gVFJVRSwgCiAgICAgICAgc2Nyb2xsQ29sbGFwc2UgPSBUUlVFKSkKYGBgCgojIyMgQ29yZSBtaWNyb2Jpb21lCgpCZWZvcmUgZ2V0dGluZyBrbmVlIGRlZXAgaW4gdGhlIERBIEFTViBhbmFseXNpcyBsZXRzIHNlZSBpZiB3ZSBjYW4ndCBpZGVudGlmeSBzb21lIGNvcmUgZWxlbWVudHMsIG9yIEFTVnMsIHRvIHRoZXNlIGZpc2guIEZpcnN0IHdlIG5lZWQgYSBtb3RodXItZm9ybWF0dGVkIGAuc2hhcmVkYCBmaWxlLiAKCgpgYGB7ciBnZXRfY29yZV9zaGFyZWRfZmlsZX0KY20gPC0gcmVhZC50YWJsZSgiUl9PVVRQVVQvc2VxX3RhYl9mb3JfY29yZS50eHQiLCBzZXAgPSAiXHQiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXM9MSkKCmNtX3QgPC0gdChjbSkgCmNsYXNzKGNtX3QpCmNtX2RmIDwtIGFzLmRhdGEuZnJhbWUoY21fdCkKY2xhc3MoY21fZGYpCm51bWNvbHMgPC0gbmNvbChjbV9kZikKY21fZGYgPC0gY21fZGYgJT4lIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJHcm91cCIpCmNtX2RmIDwtIGNtX2RmICU+JSBtdXRhdGUobGFiZWwgPSAwLjAzLCBudW1PdHVzID0gbnVtY29scykgJT4lIHNlbGVjdChsYWJlbCwgR3JvdXAsIG51bU90dXMsIGV2ZXJ5dGhpbmcoKSkKd3JpdGUudGFibGUoY21fZGYsICJSX09VVFBVVC9wc19zbHZfd29ya19maWx0LnR4dCIsIHF1b3RlID0gRkFMU0UsIHNlcCA9ICJcdCIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyMjI0NPTUJJTkUgYnkgZmlzaCBzcGVjaWVzCgpjbV9NZXJnZSA8LSBjbSAlPiUgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oIkFTViIpCgpjbV9NZXJnZSA8LSBjbV9NZXJnZSAlPiUgbXV0YXRlKEFjQ29lID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFjQ29lMDErQWNDb2UwMitBY0NvZTAzK0FjQ29lMDQrQWNDb2UwNSsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWNDb2UwNitBY0NvZTA3K0FjQ29lMDgpCmNtX01lcmdlIDwtIGNtX01lcmdlICU+JSBtdXRhdGUoQWNUcmEgPSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQWNUcmEwMStBY1RyYTAyK0FjVHJhMDMrQWNUcmEwNCtBY1RyYTA1KwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBY1RyYTA2K0FjVHJhMDcrQWNUcmEwOCtBY1RyYTA5KQpjbV9NZXJnZSA8LSBjbV9NZXJnZSAlPiUgbXV0YXRlKFNjVHJhID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNjVGFlMDErU2NUYWUwMitTY1RhZTAzK1NjVGFlMDQrU2NUYWUwNSsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU2NUYWUwNitTY1RhZTA3K1NjVGFlMDgrU2NUYWUwOSkKY21fTWVyZ2UgPC0gY21fTWVyZ2UgJT4lIG11dGF0ZShTcEF1ciA9IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTcEF1cjAxK1NwQXVyMDIrU3BBdXIwMytTcEF1cjA0K1NwQXVyMDUrCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNwQXVyMDYrU3BBdXIwNytTcEF1cjA4K1NwQXVyMDkrU3BBdXIxMCsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3BBdXIxMStTcEF1cjEyK1NwQXVyMTMpCmNtX01lcmdlIDwtIGNtX01lcmdlICU+JSBtdXRhdGUoU3BWaXIgPSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3BWaXIwMStTcFZpcjAyK1NwVmlyMDMrU3BWaXIwNCtTcFZpcjA1KwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTcFZpcjA2K1NwVmlyMDcrU3BWaXIwOCtTcFZpcjA5K1NwVmlyMTArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNwVmlyMTEpCgpjbV9NZXJnZSA8LSBjbV9NZXJnZSAlPiUgc2VsZWN0KEFTViwgQWNDb2UsIEFjVHJhLCBTY1RyYSwgU3BBdXIsIFNwVmlyKQpjbV9NZXJnZV8yIDwtIGNtX01lcmdlWywtMV0Kcm93bmFtZXMoY21fTWVyZ2VfMikgPC0gY21fTWVyZ2VbLDFdCgpjbV9NZXJnZV8yX3QgPC0gdChjbV9NZXJnZV8yKQpjbGFzcyhjbV9NZXJnZV8yX3QpCmNtX01lcmdlXzJfZGYgPC0gYXMuZGF0YS5mcmFtZShjbV9NZXJnZV8yX3QpCmNsYXNzKGNtX01lcmdlXzJfZGYpCgpjbV9NZXJnZV8yX2RmIDwtIGNtX01lcmdlXzJfZGYgJT4lIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJHcm91cCIpCmNtX01lcmdlXzJfZGYgPC0gY21fTWVyZ2VfMl9kZiAlPiUgbXV0YXRlKGxhYmVsID0gMC4wMywgbnVtT3R1cyA9IG51bWNvbHMpICU+JSBzZWxlY3QobGFiZWwsIEdyb3VwLCBudW1PdHVzLCBldmVyeXRoaW5nKCkpCndyaXRlLnRhYmxlKGNtX01lcmdlXzJfZGYsICJSX09VVFBVVC9wc19zbHZfd29ya19maWx0X2NvbWJpbmUudHh0IiwgcXVvdGUgPSBGQUxTRSwgc2VwID0gIlx0Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgpgYGAKCk5leHQgd2UgdXNlIHRoZSBvdXRwdXQgdG8gcnVuIGBnZXQuY29yZW1pY3JvYmlvbWVgIGluIG1vdGh1ci4KCgoKCioqKgoKIyMjIFNlYXJjaGluZyBwdWJsaWMgZGF0YWJhc2VzCgpOZXh0IHdlIHdhbnRlZCB0byBrbm93IHdoZXJlIGVsc2UgdGhlc2UgQVNWcyBoYWQgYmVlbiBkZXRlY3RlZCBpbiBuYXR1cmUuIFRoZXJlIGlzIGEgaHVnZSB3ZWFsdGggb2YgcHVibGljbHkgYXZhaWxhYmxlIHNlcXVlbmNlIGluZm9ybWF0aW9uIGZyb20gbWFueSBzdHVkaWVzIGFuZCBoYWJpdGF0cy4gV2UgY2FuIHVzZSB0aGlzIGluZm9ybWF0aW9uIHRvIGdldCBhIGJldHRlciBpZGVhIG9mIHRoZSBkaXN0cmlidXRpb24gYW5kIGhhYml0YXQgc3BlY2lmaWNpdHkgb2YgdGhlIERBIEFTVnMuIFRvIGFjY29tcGxpc2ggdGhpcyB3ZSBwZXJmb3JtZWQgdGhlIGZvbGxvd2luZyBzdGVwczoKCjEuIEZpcnN0IHdlIG5lZWRlZCBhIHBoeWxvc2VxIG9iamVjdCB0aGF0IG9ubHkgY29udGFpbmVkIHRoZSBEQSBBU1ZzLiBUbyBkbyB0aGlzLCB3ZSBwYXNzZWQgYW4gb2JqZWN0IGNvbnNpc3Rpbmcgb2YganVzdCB0aGVzZSA1OSBBU1ZzIChmcm9tIHRoZSBMRWZTZSBhbmFseXNpcykgdG8gdGhlIHBoeWxvc2VxIGZ1bmN0aW9uIGBwcnVuZV90YXhhYC4gIFdlIG5lZWRlZCB0d28gZGlmZmVyZW50IGBwc2Agb2JqZWN0cywgb25lIGZyb20gdGhlIHVubWVyZ2VkIG9iamVjdCAoYHBzX3Nsdl93b3JrX2ZpbHRgKSBhbmQgdGhlIG90aGVyIGZyb20gdGhlIG1lcmdlZC1ieS1nZW51cyBvYmplY3QgKGBtZXJnZWRHUGApLiAKCjIuIE5leHQgd2UgbmVlZGVkIGEgZmFzdGEgZmlsZSBvZiBvdXIgREEgQVNWcy4gV2UgY291bGQgbm90IGZpbmQgYW4gZWFzeSB3YXkgdG8gZXhwb3J0IGEgZmFzdGEgZmlsZSBmcm9tIHRoZSBuZXcgYHBzYCBvYmplY3RzLiBTbyB3ZSB0cmllZCB0aGlzIHVzaW5nIHRoZSBgdGF4X3RhYmxlYC4gVGhpcyBhcHByb2FjaCB3b3JrcyBidXQsIHdlbGwsIGl0IGlzIG5vdCB2ZXJ5IGVsZWdhbnQuIElmIHlvdSB3YW50IGEgZmFzdGEgZmlsZSBmcm9tIGFueSBvdGhlciBgcHNgIG9iamVjdHMganVzdCBzd2lwZSBvdXQgdGhlIG5hbWUgb2YgdGhlIGBwc2Agb2JqZWN0IGluIHRoZSBjb2RlIGJlbG93LiBBbnl3YXksIHdlIHdpbGwgZ2VuZXJhdGUgYW5kIHNhdmUgYSBmYXN0YSBmaWxlLgoKIyMjIyMgKipQbGVhc2Ugbm90ZSoqIHRoYXQgb24gdGhlIG1hYyB3ZSB1c2VkIHRvIGFuYWx5emUgdGhlIGRhdGEsIGZvciBzb21lIHJlYXNvbiwgc2F2ZXMgdGhlIGZhc3RhIGZpbGUgd2l0aCBgTGluZSBCcmVhayBUeXBlYCBhcyBgTGVnYWN5IE1hYyhDUilgLiBUaGlzIG1heSBiZSBpbmNvbXBhdGlibGUgd2l0aCBvdGhlciBwcm9ncmFtcyBhbmQgbmVlZHMgdG8gYmUgY2hhbmdlZCB0byBgVU5JWCAoTFMpYC4KCgpgYGB7ciBmaWxlc19mb3JfcHVibGljX3NlYXJjaH0KIyBPYmplY3Qgb2YgREEgQVNWcwpsZWZzZV9hc3ZzIDwtIGMoIkFTVjIxIiwgIkFTVjI1IiwgIkFTVjM1IiwgIkFTVjQ0IiwgIkFTVjE1OSIsICJBU1YxNyIsIAogICAgIkFTVjE3NCIsICJBU1YyMiIsICJBU1YyMzQiLCAiQVNWNjAiLCAiQVNWMTE0IiwgIkFTVjI2OCIsICJBU1YxNCIsIAogICAgIkFTVjIyNiIsICJBU1YyMyIsICJBU1YyOSIsICJBU1YzMCIsICJBU1Y0OCIsICJBU1Y5MCIsICJBU1Y5OCIsICJBU1YxOCIsIAogICAgIkFTVjQxIiwgIkFTVjciLCAiQVNWNDMiLCAiQVNWNSIsICJBU1Y1NCIsICJBU1Y4IiwgIkFTVjkiLCAiQVNWMjUwIiwgCiAgICAiQVNWMTIiLCAiQVNWMzIiLCAiQVNWMzQiLCAiQVNWMzkiLCAiQVNWMjI0IiwgIkFTVjM5OCIsICJBU1YxMjciLCAiQVNWMTI4IiwgCiAgICAiQVNWMTUxIiwgIkFTVjMyMyIsICJBU1YzNTkiLCAiQVNWMzc0IiwgIkFTVjkxIiwgIkFTVjU2IiwgIkFTVjYiLCAiQVNWMTY1IiwgCiAgICAiQVNWMjg0IiwgIkFTVjM5NSIsICJBU1Y0NTAiLCAiQVNWMTUiLCAiQVNWMiIsICJBU1YyMCIsICJBU1YyOTgiLCAiQVNWNTciLCAKICAgICJBU1Y2OSIsICJBU1Y3NSIsICJBU1Y4MiIsICJBU1YxIiwgIkFTVjcwIiwgIkFTVjQ5IikKIyBDcmVhdGUgcHMgb2JqZWN0cwpkYV9hc3ZzIDwtIHBydW5lX3RheGEobGVmc2VfYXN2cywgbWVyZ2VkR1ApCmRhX2FzdnNfZnVsbCA8LSBwcnVuZV90YXhhKGxlZnNlX2FzdnMsIHBzX3Nsdl93b3JrX2ZpbHQpCiMgQ3JlYXRlIGZhc3RhIGZpbGUgZnJvbSB0YXhfdGFibGUKdGFibGUyZm9ybWF0IDwtIHRheF90YWJsZShkYV9hc3ZzKQojcmV0YWluIG9ubHkgdGhlIGNvbHVtbiB3aXRoIHRoZSBzZXF1ZW5jZXMKdGFibGUyZm9ybWF0X3RyaW0gPC0gdGFibGUyZm9ybWF0WywgN10gIAp0YWJsZTJmb3JtYXRfdHJpbV9kZiA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcyh0YWJsZTJmb3JtYXRfdHJpbSksIHRhYmxlMmZvcm1hdF90cmltKQpjb2xuYW1lcyh0YWJsZTJmb3JtYXRfdHJpbV9kZikgPC0gYygiQVNWX0lEIiwgIkFTVl9TRVEiKQp0YWJsZTJmb3JtYXRfdHJpbV9kZiRBU1ZfSUQgPC0gc3ViKCJBU1YiICwgIj5BU1YiLCB0YWJsZTJmb3JtYXRfdHJpbV9kZiRBU1ZfSUQpICNmb3JtYXQgZmFzdGEKd3JpdGUudGFibGUodGFibGUyZm9ybWF0X3RyaW1fZGYsICJSX09VVFBVVC9BU1ZfRk9SX0JMQVNULmZhc3RhIiwgc2VwID0gIlxyIiwgCiAgICBjb2wubmFtZXMgPSBGQUxTRSwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UsIGZpbGVFbmNvZGluZyA9ICJVVEYtOCIpCmBgYAoKMy4gV2l0aCBvdXIgbmV3bHkgY3JlYXRlZCBEQSBBU1YgZmFzdGEgZmlsZSB3ZSBjYW4gbW92ZSBvbiB0byBkYXRhYmFzZSBzZWFyY2hpbmcuIFdlIHVzZWQgIFtCTEFTVG5dKGh0dHBzOi8vYmxhc3QubmNiaS5ubG0ubmloLmdvdi9CbGFzdC5jZ2k/UFJPR1JBTT1ibGFzdG4mUEFHRV9UWVBFPUJsYXN0U2VhcmNoJkxJTktfTE9DPWJsYXN0aG9tZSl7dGFyZ2V0PSJfYmxhbmsifSBhZ2FpbnN0IHRoZSAqbnIqIGRhdGFiYXNlIHRvIHNlYXJjaCBwdWJsaWNseSBhdmFpbGFibGUgc2VxdWVuY2UgZGF0YS4gV2UgdXNlZCB0aGVzZSBzZXR0aW5nczogCiAgICArIG9wdGltaXplZCBmb3IgaGlnaGx5IHNpbWlsYXIgc2VxdWVuY2VzIChtZWdhYmxhc3QpIAogICAgKyBFeHBlY3QgdGhyZXNob2xkID0gMTAKICAgICsgV29yZCBzaXplID0gMjgKICAgICsgTWF0Y2gvTWlzbWF0Y2ggU2NvcmVzID0gMSwgLTIKICAgICsgR2FwIENvc3RzID0gbGluZWFyLgogICAgKyByZXRhaW4gdG9wIDEwIGhpdHMKICAgIApIZXJlIGFyZSB0aGUgdG9wIEJMQVNUIGhpdHMgZm9yIGVhY2ggREEgQVNWLiBUaGUgdGFibGUgZGlzcGxheXMgYSBsb3Qgb2YgaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBCTEFTVCBzZWFyY2ggKGl0IHNjcm9sbHMgYWxvbmcgdGhlIHgtYXhpcyBieSB0aGUgd2F5KS4gTW9zdCBpbXBvcnRhbnRseSBhcmUgdGhlIGFjY2Vzc2lvbiBudW1iZXJzIG9mIHRvcCBCTEFTVCBoaXRzIChzdWJqZWN0IGFjYy52YXIpLCBudW1iZXIgb2YgMTAwJSBpZGVudGljYWwgbWF0Y2hlcyAobnVtIHBlcmZlY3QgaGl0cyksIHRoZSBwZXJjZW50IGlkZW50aXR5LCBhbmQgc29tZSBpbmZvIG9uIHdoZXJlL3doZW4gdGhlIGhpdCBzZXF1ZW5jZSB3YXMgb3JpZ2luYWxseSBmb3VuZC4gV2hlcmUgYXBwbGljYWJsZSwgdGhlcmUgaXMgYWxzbyBQdWJNZWRJRHMgc28geW91IGNhbiBmaW5kIHRoZSBwYXBlciB0aGF0IHJlcG9ydGVkIHRoZSBzZXF1ZW5jZS4gTG9va2luZyBhdCB0aGlzIHRhYmxlIHdpbGwgZ2l2ZSB5b3UgYSBwcmVsaW1pbmFyeSBzZW5zZSBvZiB0aGUgZWNvbG9neSBvZiB0aGVzZSBBU1ZzLiBGb3IgZXhhbXBsZSwgbW9zdCBoaXRzIGNvbWUgZnJvbSBpbnRlc3RpbmFsIGNvbW11bml0aWVzLCBtYW55IG9mIHdoaWNoIGFyZSBtYXJpbmUgaGVyYml2b3JvdXMgZmlzaC4gQnV0IHRoZSBsb3cgcGVyY2VudCBpZGVudGl0eSBvZiBzZXZlcmFsIEFTVnMgaW5kaWNhdGVzIHRoYXQgdGhlc2Ugc2VxdWVuY2VzIGhhdmUgYmVlbiBwb29ybHkgc2FtcGxlZC4gVGhpcyBpcyBub3Qgc3VycHJpc2luZyBnaXZlbiB0aGUgZ2VvZ3JhcGhpYyBza2V3IG9mIHNhbXBsaW5nLgoKKioqCgojIyMjIFRvcCBoaXRzIGZyb20gIEJMQVNUbiBhbmFseXNpcwoKKipOb3RlKiogKlRoaXMgdGFibGUgYWxzbyBzY3JvbGxzIGhvcml6b250YWxseS4qIAoKIyMjIzxmb250IGNvbG9yPSJyZWQiPlN1cHBsZW1lbnRhcnkgVGFibGUgNjwvZm9udD4KCmBgYHtyIGJsYXN0X3RhYmxlfQpibGFzdF90YWIgPC0gcmVhZC50YWJsZSgiTUFOVUFMX0lOUFVUL0JMQVRfcmVzdWx0cy50eHQiLCBoZWFkZXIgPSBUUlVFLCAKICAgIHNlcCA9ICJcdCIsIGNoZWNrLm5hbWVzID0gRkFMU0UpCndyaXRlLnRhYmxlKGJsYXN0X3RhYiwgIlJfT1VUUFVUL1N1cHBsZW1lbnRhcnlfVGFibGVfNi50eHQiLCByb3cubmFtZXMgPSBGQUxTRSwgc2VwID0gIlx0IiwgcXVvdGUgPSBGQUxTRSkKZGF0YXRhYmxlKGJsYXN0X3RhYiwgcm93bmFtZXMgPSBGQUxTRSwgY2FwdGlvbiA9IGh0bWx0b29sczo6dGFncyRjYXB0aW9uKHN0eWxlID0gImNhcHRpb24tc2lkZTogYm90dG9tOyB0ZXh0LWFsaWduOiBsZWZ0OyIsIAogICAgIlN1cHBsZW1lbnRhcnkgVGFibGUgNjogIiwgaHRtbHRvb2xzOjplbSgiUmVzdWx0cyBvZiBCTEFTVG4gYW5hbHlzaXMuIikpLCBleHRlbnNpb25zID0gIkZpeGVkQ29sdW1ucyIsIAogICAgIkJ1dHRvbnMiLCBvcHRpb25zID0gbGlzdChjb2x1bW5EZWZzID0gbGlzdChsaXN0KGNsYXNzTmFtZSA9ICJkdC1jZW50ZXIiLCAKICAgICAgICB0YXJnZXRzID0gYygxLCAyLCAzLCA0LCA1LCA2LCA3LCA4KSkpLCBkb20gPSAiQmxmcnRpcCIsIHBhZ2VMZW5ndGggPSA1LCAKICAgICAgICBsZW5ndGhNZW51ID0gYyg1LCAxMCwgMjUsIDY1KSwgYnV0dG9ucyA9IGMoImNzdiIsICJjb3B5IiksIHNjcm9sbFggPSBUUlVFLCAKICAgICAgICBzY3JvbGxDb2xsYXBzZSA9IFRSVUUpKQpgYGAKCgoqKioKClNldmVyYWwgQVNWcyByZXR1cm5lZCBtb3JlIHRoYW4gb25lIG1hdGNoIGF0IDEwMCUuIEZvciBzb21lIEFTVnMsIHRoZSAxMDAlIG1hdGNoZXMgd2VyZSBmcm9tIHRoZSBzYW1lIHN0dWR5L3N0dWR5IG9yZ2FuaXNtLCBzbyB3ZSBqdXN0IHNlbGVjdGVkIG9uZSBhcyB0aGUgcmVwcmVzZW50YXRpdmUuIEFTVnMgNiwgMTIsIDIyNCwgYW5kIDM5OCByZXR1cm5lZCBudW1lcm91cyAxMDAlIG1hdGNoZXMgKG91dCBvZiA1MCB0b3RhbCkuIFRoZXNlIGRhdGEgd2VyZSBpbXByYWN0aWNhbCAgdG8gc3VtbWFyaXplIGFuZCBub3QgdmVyeSBpbmZvcm1hdGl2ZSBhbnl3YXkuIFNvIHdlIGVsZWN0ZWQgdG8gbGVhdmUgdGhlc2UgZGF0YSBvdXQuIElmIHlvdSB3YW50IHRvIHNlZSB3aGF0IHRoZXNlIGhpdHMgYXJlLCBqdXN0IGdyYWIgdGhlIEFTViBzZXF1ZW5jZSBhbmQgQkxBU1QgYXdheS4gCgpBbHNvLCBzb21lIEFTVnMgc2hhcmVkIHRoZSBzYW1lIHRvIGhpdC4gU2luY2UgdGhlIHRhYmxlIGlzICdieSBBU1YnIHdlIHJldGFpbmVkIGFsbCBkdXBsaWNhdGUgaGl0cy4KCjQuIEZvciBwaHlsb2dlbmV0aWMgaW5mZXJlbmNlIHdlIHVzZWQgdGhlIFtTaWx2YSBBbGlnbm1lbnQsIENsYXNzaWZpY2F0aW9uIGFuZCBUcmVlIFNlcnZpY2VdKGh0dHBzOi8vd3d3LmFyYi1zaWx2YS5kZS9hbGlnbmVyLyl7dGFyZ2V0PSJfYmxhbmsifSB0byBvYnRhaW4gbmVpZ2hib3JzIG9mIERBIEFTVnMuIFdlIHVzZWQgdGhlc2Ugc2V0dGluZ3M6IAogICAgKyBTZWFyY2ggYW5kIGNsYXNzaWZ5OiBtaW4gaWRlbnRpdHkgPSAwLjk1OyBOdW1iZXIgb2YgbmVpZ2hib3JzID0gNS4gCiAgICArIERlZmF1bHQgcGFyYW1ldGVycyBmb3IgdGhlIHJlbWFpbmRlciBvZiB0aGUgd29ya2Zsb3cuCgo1LiBXZSB0aGVuIGNvbWJpbmVkIHRoZSByZXN1bHRzIG9mIHRoZSBCTEFTVG4gYW5kIFNpbHZhIEFDVCBhbmFseXNlcyBhbmQgb21pdHRlZCBkdXBsaWNhdGUgaGl0cywgcmVzdWx0aW5nIGluICoqMjk3KiogdG9wIGhpdCBzZXF1ZW5jZXMgZm9yIHBoeWxvZ2VuZXRpYyBhbmFseXNpcy4KCgojIyMgUGh5bG9nZW5ldGljIGluZmVyZW5jZQoKU2hvcnQgcmVhZCBzZXF1ZW5jZXMgYXJlIG5vdCBpZGVhbCBmb3IgcGh5bG9nZW5ldGljIGFuYWx5c2lzLCBidXQgZ2l2ZW4gdGhlIGRhdGEgd2UgaGF2ZSwgd2UgZmVsdCB0aGlzIHdhcyBhIGdvb2QgcGxhY2UgdG8gc3RhcnQuIFRoaXMgYW5hbHlzaXMgcmVxdWlyZWQgc2V2ZXJhbCBzdGVwcy4KCjEuIFdlIHVzZWQgW21vdGh1cl0oaHR0cHM6Ly93d3cubW90aHVyLm9yZy93aWtpLyl7dGFyZ2V0PSJfYmxhbmsifSAodi4xLjQwLjQsIExhc3QgdXBkYXRlZDogMDcvMjUvMjAxOCkgYW5kIHRoZSBbU2lsdmEgZnVsbCBsZW5ndGggc2VxdWVuY2VzIGFuZCB0YXhvbm9teSByZWZlcmVuY2VzIChyZWxlYXNlIDEzMildKGh0dHBzOi8vd3d3Lm1vdGh1ci5vcmcvd2lraS9TaWx2YV9yZWZlcmVuY2VfZmlsZXMpe3RhcmdldD0iX2JsYW5rIn0gdG8gYWxpZ24gdGhlIERBIEFTVnMgYW5kIG91ciBuZXcgcmVmZXJlbmNlIGRiLCBhbmQgYWxzbyBjbGFzc2lmeSB0aGUgc2VxdWVuY2VzLiBXZSB1c2VkIGEgW2hhcmQgbWFza10oaHR0cHM6Ly93d3cubW90aHVyLm9yZy93aWtpL0ZpbHRlci5zZXFzI2hhcmQpe3RhcmdldD0iX2JsYW5rIn0gdG8gdHJpbSBhbGwgbG9uZyByZWFkcyB0byB0aGUgc2FtZSBsZW5ndGggKH4zNzNicCkgYW5kIDE2UyByZWdpb24gYXMgdGhlIEFTVnMuIAoKYGBgCm1vdGh1ciAiI2FsaWduLnNlcXMoY2FuZGlkYXRlPXNlcXVlbmNlX3RyZWUuZmFzdGEsIHRlbXBsYXRlPXNpbHZhLm5yX3YxMzIuYWxpZ24sIHByb2Nlc3NvcnM9MjAsIGZsaXA9dCkiICAKbW90aHVyICIjZmlsdGVyLnNlcXMoZmFzdGE9c2VxdWVuY2VfdHJlZS5mYXN0YSwgdmVydGljYWwgPSA9RiwgaGFyZD1tYXNrLnR4dCkiICAKbW90aHVyICIjY2xhc3NpZnkuc2VxcyhmYXN0YT1zZXF1ZW5jZV90cmVlLmZpbHRlci5mYXN0YSwgdGVtcGxhdGU9c2lsdmEubnJfdjEzMi5hbGlnbiwgdGF4b25vbXk9c2lsdmEubnJfdjEzMi50YXgsIHByb2Nlc3NvcnM9MTApIiAgCmBgYAoyLiBOb3cgdGhhdCB3ZSBoYWQgb3VyIDU5IERBIEFTVnMgYW5kIHRoZSB0b3AgZGF0YWJhc2UgaGl0cywgaXRzIHdhcyB0aW1lIGZvciBwaHlsb2dlbmV0aWMgaW5mZXJlbmNlLiBXZSB1c2VkIFtSQXhNTC1IUENdKGh0dHBzOi8vc2NvLmgtaXRzLm9yZy9leGVsaXhpcy93ZWIvc29mdHdhcmUvcmF4bWwvaW5kZXguaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSB0byBnZW5lcmF0ZSBhIHBoeWxvZ2VuZXRpYyB0cmVlICh3aXRoICpBcXVpZmV4KiBhcyB0aGUgb3V0Z3JvdXApCgpgYGAKcmF4bWxIUEMtUFRIUkVBRFMtU1NFMyAtVCAyNCAtZiBhIC1wIDIzNDUgLXggMzQ1NiAtbSBHVFJHQU1NQSAgLU4gMTAwMCAtcyBzZXF1ZW5jZV90cmVlLmZpbHRlci5mYXN0YSAtbiBmaXNoX2FyYl9hbGlnbl8xMDAwQlNfQi50cmUKYGBgCgo8YSBpZD0iUGFydCBWOiBTeW50aGVzaXMiPjwvYT4KCiMjUGFydCBWOiBTeW50aGVzaXMKCltiYWNrIHRvIHRvcF0oI2JhY2sgdG8gdG9wKQoKTm93IGl0IHdhcyB0aW1lIHRvIHB1dCB0aGUgcGllY2VzIHRvZ2V0aGVyIGFuZCBkZXRlcm1pbmUuLi4KCj4gV2hhdCB0aGVzZSBwYXR0ZXJucyB0ZWxsIHVzIGFib3V0IHNwZWNpZmljaXR5PyAKCiMjIyBWaXN1YWxpemluZyB0aGUgcGh5bG9nZW5ldGljIHRyZWUKCiMjIyMgSU1OR1MgYW5hbHlzaXMKClRvIGRpZyBqdXN0IGEgbGl0dGxlIGRlZXBlciwgd2Ugc2NyZWVuZWQgb3VyIERBIEFTVnMgYWdhaW5zdCB0aGUgW0lNTkdTIGRhdGFiYXNlXShodHRwczovL3d3dy5pbW5ncy5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9LiBJTU5HUyBob3N0cyBhIGN1cmF0ZWQgZGF0YWJhc2Ugb2Ygc2hvcnQtcmVhZCBzZXF1ZW5jZXMgc2NyYXBlZCBmcm9tIHRoZSBJbnRlcm5hdGlvbmFsIE51Y2xlb3RpZGUgU2VxdWVuY2UgRGF0YWJhc2UgQ29sbGFib3JhdGlvbiAoR2VuQmFuaywgRERCSiBhbmQgRU1CTCkuIFRoZSBkYXRhYmFzZSBpcyByZWJ1aWx0IG1vbnRobHkgYW5kIGF0IHRoZSB0aW1lIG9mIHRoaXMgYW5hbHlzaXMsIGNvbnRhaW5lZCAyNzEsMjM3IHNhbXBsZXMuIElNTkcgaXMgcmVhbGx5IGRlc2lnbmVkIHRvIHNjcmVlbiBmdWxsLWxlbmd0aCAxNlMgclJOQSBzZXF1ZW5jZXMgYW5kIGlzIG5vdCBpZGVhbCBmb3Igc2hvcnRlciByZWFkcy4gVGhpcyBpcyBiZWNhdXNlIHRoZSBkYXRhYmFzZSBpcyBidWlsdCBmcm9tIHNob3J0IHJlYWRzLCBhbmQgZGlmZmVyZW50IHN0dWRpZXMgdGFyZ2V0IGRpZmZlcmVudCByZWdpb25zIG9mIHRoZSBnZW5lLiBGb3IgZXhhbXBsZSwgaWYgd2UgZ2V0IG5vIGhpdHMgdG8gYW4gQVNWIGl0IGNvdWxkIG1lYW4gdGhlIG9yZ2FuaXNtcyBpdCBjYW1lIGZyb20gaGFzIHJlYWxseSBub3QgYmVlbiBkZXRlY3RlZCBiZWZvcmUgb3IgdGhhdCBpdCBpcyBpbiB0aGUgZGF0YWJhc2UgYnV0IGlzIHJlcHJlc2VudGVkIGJ5IGEgZGlmZmVyZW50IDE2UyByZWdpb24uIFNvIHRha2UgdGhlc2UgZGF0YSB3aXRoIGEgZ3JhaW4gb2Ygc2FsdC4gCgoqKklNTkdTIGlzIG5vdCBhIGhpZ2gtdGhyb3VnaHB1dCBzeXN0ZW0qKi4gVXNlciBtYXkgb25seSBzdWJtaXQgYSBtYXhpbXVtIG9mIDEwIHNlcXVlbmNlcyBwZXIgcXVlcnkgYW5kIHRoaXMgY2FuIChhbmQgd2lsbCkgdGFrZSB3ZWVrcyB0byBydW4uIFNvIGNob29zZSB5b3VyIEFTVnMgY2FyZWZ1bGx5LiAKCklNTkdTIHdpbGwgcmV0dXJuIGEgbG90IG9mIHVzZWZ1bCBkYXRhIGZvciBlYWNoIHF1ZXJ5IHNlcXVlbmNlLiBBbGwgd2Ugd2VyZSBpbnRlcmVzdGVkIGluIGhlcmUgd2FzIHRoZSBudW1iZXIgb2YgaGl0cywgYnV0IHRoZXJlIG11Y2ggbW9yZSByZWFsbHkgdXNlZnVsIGRhdGEgaGVyZS4gQW1vbmcgb3RoZXIgZGF0YSBwcm9kdWN0cywgSU1OR1MgcmV0dXJucyByZXBvcnQgdGFibGVzIHRoYXQgdGFsbHkgdGhlICpudW1iZXIgb2Ygc2FtcGxlcyB0aGF0IHdlcmUgcG9zaXRpdmUgZm9yIHRoZSBwcmVzZW5jZSBvZiBxdWVyeS1saWtlIHNlcXVlbmNlcyBmb3IgZWFjaCBzYW1wbGUgY2F0ZWdvcnkqLS0tY2F0ZWdvcmllcyBsaWtlICpzaHJpbXAgZ3V0IG1ldGFnZW5vbWUqIGFuZCAqc2Vhd2F0ZXIgbWV0YWdlbm9tZSouIEVhY2ggY2F0ZWdvcnkgaGFzIGEgbnVtYmVyIG9mIHNob3J0LXJlYWQgc2FtcGxlcywgd2hpY2ggY291bGQgb3JpZ2luYXRlIGZyb20gYSBzaW5nbGUgc3R1ZHkgb3IgbXVsdGlwbGUgc3R1ZGllcy4gCgpBIHJlcG9ydCBpbmNsdWRlcyB2YWx1ZXMgZm9yIHNldmVyYWwgcGVyY2VudCBpZGVudGl0eSBjdXRvZmYgdmFsdWVzLiBXZSBzZXQgYSBtaW5pbXVtIHRocmVzaG9sZCBhdCA5NyUgc28gb3VyIHJlcG9ydHMgaGF2ZSB2YWx1ZXMgZm9yIDk3IGFuZCA5OSUuIFlvdSBjYW4gc2V0IHRoZSB0aHJlc2hvbGQgYXMgbG93IGFzIDkwJS4gCgpJTU5HUyBwcm92aWRlcyB0aHJlZSBzdWNoIHJlcG9ydHMgYmFzZWQgb24gdGhlIGFidW5kYW5jZSBvZiB5b3VyIHF1ZXJ5IHNlcXVlbmNlLiAKCjEuIEFuIFNSQS1kZXJpdmVkIHNhbXBsZSBpcyBjb25zaWRlcmVkIHBvc2l0aXZlIGlmIHRoZSBxdWVyeS1saWtlIHNlcXVlbmNlcyBzdW0gdXAgdG8gbW9yZSB0aGFuICoqMCUqKiBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIHNlcXVlbmNlcyBpbiB0aGF0IHNhbXBsZSAoaS5lLiBhbnkgYWJ1bmRhbmNlKS4KCjIuIEFuIFNSQS1kZXJpdmVkIHNhbXBsZSBpcyBjb25zaWRlcmVkIHBvc2l0aXZlIGlmIHRoZSBxdWVyeS1saWtlIHNlcXVlbmNlcyBzdW0gdXAgdG8gbW9yZSB0aGFuICoqMC4xJSoqIG9mIHRoZSB0b3RhbCBudW1iZXIgb2Ygc2VxdWVuY2VzIGluIHRoYXQgc2FtcGxlIChpLmUuIGV4Y2x1ZGluZyByYXJlIGFidW5kYW5jZXMpLgoKMy4gQW4gU1JBLWRlcml2ZWQgc2FtcGxlIGlzIGNvbnNpZGVyZWQgcG9zaXRpdmUgaWYgdGhlIHF1ZXJ5LWxpa2Ugc2VxdWVuY2VzIHN1bSB1cCB0byBtb3JlIHRoYW4gKioxJSoqIG9mIHRoZSB0b3RhbCBudW1iZXIgb2Ygc2VxdWVuY2VzIGluIHRoYXQgc2FtcGxlIChpLmUuIGluY2x1ZGluZyBvbmx5IGRvbWluYW50IE9UVXMpLgoKV2UgcmVwb3J0IGRhdGEgZnJvbSA5NyUgY3V0b2ZmIGlkZW50aXR5IGFuZCAwLjElIG9mIHRvdGFsIHJlYWRzIGluIGEgc2FtcGxlLiBJIHRoaW5rIHRoaXMgaXMgYSBsaXR0bGUgY29uZnVzaW5nIHNvIGxldCBtZSBleHBsYWluIGJ5IGV4YW1wbGUuIEluIHRoZSB0cmVlIGJlbG93LCBBU1YzOTggaXMgbW9zdCBjbG9zZWx5IHJlbGF0ZWQgdG8gYW4gQWxwaGFwcm90ZW9iYWN0ZXJpYSBhc3NvY2lhdGVkIHdpdGggdGhlIHRveGljIGJlbnRoaWMgbWFyaW5lIGRpbm9mbGFnZWxsYXRlLCAqT3N0cmVvcHNpcyBvdmF0YSouIFdlIHJldHJpZXZlZCB0aGlzIHNlcXVlbmNlIGR1cmluZyB0aGUgQkxBU1RuIGFuYWx5c2lzIGRpc2N1c3NlZCBhYm92ZS4gQW55d2F5LCBBU1YzOTggd2FzIHNjcmVlbmVkIGFnYWluc3QgSU1OR1MgYW5kIHJldHVybmVkIDI0NjAgaGl0cy4gVGhpcyBtZWFucyB0aGF0IGF0IDk3JSBpZGVudGl0eSwgMjQ2MCBzYW1wbGVzIGhhZCBhbiBBU1YzOTgtbGlrZSBzZXF1ZW5jZSBjb21wcmlzaW5nIGdyZWF0ZXIgdGhhbiAwLjElIG9mIGEgZ2l2ZW4gc2FtcGxlcyB0b3RhbCBudW1iZXIgb2Ygc2VxdWVuY2VzLiBJZiBmb3IgZXhhbXBsZSB3ZSBpbmNyZWFzZSB0aGUgcGVyY2VudCBpZGVudGl0eSB0byA5OSUgdGhlIG51bWJlciBvZiBzYW1wbGUgaGl0cyBkcm9wcyB0byAxMzguIElmIGluc3RlYWQgd2UgbG9vayBhdCB0aGUgMCUgcmVwb3J0ICg5NyUgaWRlbnRpdHkpLCB0aGUgbnVtYmVyIG9mIHNhbXBsZSBoaXRzIGluY3JlYXNlcyB0byA2MzIzLiAKCiMjIyMgU3VsbGFtIGxpZmVzdHlsZSBjYXRlZ29yaWVzCgpXZSBhbHNvIGNvbXBhcmVkIHRoZSBmaW5hbCBsaXN0IG9mIHRvcCBoaXRzIHRvIHRoZSBbU3VsbGFtIGV0LiBhbC5dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTExL2ouMTM2NS0yOTRYLjIwMTIuMDU1NTIueCl7dGFyZ2V0PSJfYmxhbmsifSBwYXBlciwgc3BlY2lmaWNhbGx5ICoqVGFibGUgUzEgKiogZnJvbSB0aGF0IHBhcGVyLiBCZWNhdXNlIHRoaXMgcGFwZXIgd2FzIHB1Ymxpc2hlZCBpbiAyMDEyLCB0aGVyZSB3ZXJlIG1hbnkgc2VxdWVuY2UgaGl0cyBpbiBvdXIgZGIgdGhhdCBkaWQgbm90IGFwcGVhciBpbiB0aGUgb3JpZ2luYWwgcGFwZXIuIEhvd2V2ZXIsIGZvciB0aG9zZSB0aGF0IGRpZCwgd2UgYWRkZWQgdGhlIFN1bGxhbSAqbGlmZXN0eWxlIGNhdGVnb3J5KiBkZXNpZ25hdGlvbnMgdG8gdGhlIHRyZWUgbWV0YWRhdGEuIAoKIyMjIyBQdXR0aW5nIHRoZSBwaWVjZXMgdG9nZXRoZXIKCldlIHRvb2sgYWxsIG9mIHRoZXNlIGRhdGEgYW5kIHVzZWQgW2lUT0xdKGh0dHBzOi8vaXRvbC5lbWJsLmRlLyl7dGFyZ2V0PSJfYmxhbmsifSB0byB2aXN1YWxpemUgdGhlIHRyZWUuIEZvciBlYWNoIHRvcCBoaXQsIHdlIGFkZGVkIHRoZSBpc29sYXRpb24gc291cmNlL25hdHVyYWwgaG9zdCBpbmZvcm1hdGlvbiwgdGF4b25vbWljIGFmZmlsaWF0aW9uLCBhbmQgU3VsbGFtICpsaWZlc3R5bGUgY2F0ZWdvcnkqLiBXZSBhbHNvIG92ZXJsYWlkIHRoZSBudW1iZXIgb2YgaGl0cyB0byB0aGUgSU1OR1MgZGF0YWJhc2UgZm9yIGVhY2ggQVNWLgoKVG8gdmlldyBhIGZ1bGwsIGludGVyYWN0aXZlIHZlcnNpb24gb2YgdGhlIHRyZWUgZ28gdGhpcyBbaVRPTCBwYWdlXShodHRwczovL2l0b2wuZW1ibC5kZS90cmVlLzE4NjE0ODk4MTg2MzkyMjAxNTM1NDk2NTQxKXt0YXJnZXQ9Il9ibGFuayJ9LgoKKioqCgojIyMjIFpvb21hYmxlIGlUT0wgdHJlZQoKIyMjIzxmb250IGNvbG9yPSJyZWQiPkZpZ3VyZSAzPC9mb250PgoKYGBge3IgaW5zZXJ0X2l0b2wsIG91dC53aWR0aCA9ICIxMDAlIiwgZmlnLmNhcCA9ICJGaWd1cmUgMyIsIGV2YWwgPSBGQUxTRSwgZWNobyA9IEZBTFNFfQojIFRoaXMgaXMgdG8gaW5jbHVkZSBhIHN0YXRpYyBpbWFnZQppdG9sX3RyZWUgPC0ga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIk1BTlVBTF9JTlBVVC9GaWd1cmVfM0Euc3ZnIikKaXRvbF90cmVlCmBgYAoKKioqCgpgYGB7ciB6b29tLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLmFsaWduID0gImNlbnRlciIsIG91dC53aWR0aCA9ICIxMDAlIn0KIyBOb3RlOiBpbiBvcmRlciBmb3IgdGhpcyB0byB3b3JrLCBlc3BlY2lhbGx5IGlmIHRoZSBTVkcgZmllIGhhcyBiZWVuIG1hbmlwdWxhdGVkIGluIElua3NjYXBlLCAKIyBpdCBpcyB3aXNlIHRvIGRvIHRoZSBmb2xsb3dpbmc6IDEuIGRvIHdoYXRldmVyIHlvdSBuZWVkIGRvLCAyLiByZXNpemUgdG8gZHJhd2luZywgCiMzLiBTYXZlIGEgY29weSwgIDQuIHNhdmUgYXMgT3B0aW1pemVkIFNWRwppdG9sX3RyZWUgPC0gc3ZnUGFuWm9vbSgiTUFOVUFMX0lOUFVUL0ZpZ3VyZV8zX1RyZWVBLnN2ZyIsIGNvbnRyb2xJY29uc0VuYWJsZWQgPSBUUlVFKQppdG9sX3RyZWUKYGBgCgpgYGB7ciB0cmVlX2xlZ2VuZCwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQppdG9sX2xlZ2VuZCA8LSBrbml0cjo6aW5jbHVkZV9ncmFwaGljcygiTUFOVUFMX0lOUFVUL0ZpZ3VyZV8zX0xlZ2VuZF9BLnN2ZyIpCml0b2xfbGVnZW5kCmBgYAoKIyMjIEluZmVycmluZyBsaWZlc3R5bGUgY2F0ZWdvcnkKCldlIHVzZWQgdGhlIHRyZWUgdG8gaW5mZXIgdGhlICpsaWZlc3R5bGUgY2F0ZWdvcnkqIG9mIGVhY2ggQVNWcyBiYXNlZCBvbiB0aGUgIGNsb3Nlc3QgcmVsYXRpdmVzIGluIHRoZWlyIGNsYWRlLiBUaGlzIHdhcyBub3QgYSBxdWFudGl0YXRpdmUsIGJ1dCByYXRoZXIgYSB1c2VyIGd1aWRlZCBkZXRlcm1pbmF0aW9uLiBBc2lkZSBmcm9tIE1ldGFNZXRhRGIgKGRpc2N1c3NlZCBhYm92ZSkgd2UgYXJlIG5vdCBhd2FyZSBvZiBhbnkgdG9vbCBjdXJyZW50bHkgYXZhaWxhYmxlIHRvIHF1YW50aXRhdGl2ZWx5IGFzc2VzcyBoYWJpdGF0IHByZWZlcmVuY2Ugb2YgYSAxNlMgclJOQSBzZXF1ZW5jZS4KCkZvciBzaW1wbGljaXR5IHdlIGZvY3VzZWQgb24gdGhyZWUgKmxpZmVzdHlsZSBjYXRhZ29yaWVzKiAodGhvdWdoIHdlIGhhdmUgc2V2ZW4gY2F0ZWdvcmllcyBpbiB0aGUgdHJlZSkuIE91ciByZWFzb25pbmctLS1hZ2FpbiBiYXNlZCBvbiB0aGUgd29yayBvZiBTdWxsYW0gZXQuIGFsLi0tLXdhcyB0aGF0IGZpc2ggaW50ZXN0aW5lcyBjb250YWluIG1pY3JvYmVzIHRoYXQgY29tZSBmcm9tIHRoZSBlbnZpcm9ubWVudCAod2hhdCB0aGV5IGVhdCwgd2hlcmUgdGhleSBsaXZlKSwgbWljcm9iZXMgdGhhdCBhcmUgdGhlcmUgYmVjYXVzZSBmaXNoIGFyZSBhbmltYWxzIHdpdGggZ3V0cywgYW5kIG1pY3JvYmVzIHRoYXQgYXJlIHRoZXJlIGJlY2F1c2UgZmlzaCBhcmUgZmlzaCBhbmQgaGF2ZSBhIHBoeXNpb2xvZ3kgYW5kIGV2b2x1dGlvbmFyeSBoaXN0b3J5IHRoYXQgc2VsZWN0IHNwZWNpZmljIG9yZ2FuaXNtcy4gIFN1bGxhbSBldC5hbC4gd2VyZSBhbHNvIGxvb2tpbmcgYXQgZmlzaCBmcm9tIGRpZmZlcmVudCBoYWJpdGF0cyAoZnJlc2h3YXRlciwgZXN0dWFyeSwgbWFyaW5lKSBhbmQgdHJvcGljIGxldmVscyAoY2Fybml2b3JlcywgaGVyYml2b3Jlcywgb21uaXZvcmVzKSB3aGlsZSBvdXIgc3R1ZHkgd2FzIG1vcmUgbmFycm93IGluIHNjb3BlLgoKKiAgICoqZmlzaCBhc3NvY2lhdGVkKio6IEFTVnMgbW9zdCBjbG9zZWx5IHJlbGF0ZWQgdG8gc2VxdWVuY2VzIGZyb20gdGhlIGludGVzdGluYWwgdHJhY3Qgb2YgbWFyaW5lIGZpc2guIAoqICAgKiphbmltYWwgYXNzb2NpYXRlZCoqOiBBU1ZzIG1vc3QgY2xvc2VseSByZWxhdGVkIHRvIHNlcXVlbmNlcyBmcm9tIG90aGVyIGFuaW1hbHMsIGluY2x1ZGluZyBvbmUgZnJlc2h3YXRlciBmaXNoLCBvdGhlciB2ZXJ0ZWJyYXRlcywgYW5kIGEgZmV3IG5vbi1tYXJpbmUgaW52ZXJ0ZWJyYXRlcy4gIAoqICAgKiplbnZpcm9ubWVudGFsKio6IEFTVnMgbW9zdCBjbG9zZWx5IHJlbGF0ZWQgdG8gc2VxdWVuY2VzIGZyb20gdGhlIGVudmlyb25tZW50LiBCeSBhbmQgbGFyZ2UgdGhlc2UgYXJlIG1hcmluZSBvciBtYXJpbmUtbGlrZSAoZS5nLiwgaHlwZXJzYWxpbmUgbWF0cywgc2FsaW5lIGxha2VzKSBpbiBvcmlnaW4gaW5jbHVkaW5nIHNlZGltZW50cywgd2F0ZXIsIGFuZCBwb3RlbnRpYWwgcHJleSAoYWxnYWUsIHBsYW50cywgY29yYWwsIHNwb25nZSkuIEhvd2V2ZXIgdGhlcmUgYXJlIHNvbWUgbGVhdmVzIGluIHRoZSB0cmVlIGZyb20gbm9uLW1hcmluZSBlbnZpcm9ubWVudHMgKGUuZy4sIGFjdGl2YXRlZCBzbHVkZ2UpIHRoYXQgd2UgZ3JvdXBlZCBpbiB0aGUgZW52aXJvbm1lbnRhbCBjYXRlZ29yeS4gIAoKKioqCgojIyMjIEFzc2Vzc2luZyBoYWJpdGF0IHNwZWNpZmljaXR5LgoKV2UgY29tYmluZWQgdGhlc2UgaGFiaXRhdCBwcmVkaWN0aW9ucyB3aXRoIHRoZSByZXN1bHRzIG9mIHRoZSBCTEFTVG4gYW5hbHlzaXMsIHNjYW4gb2YgdGhlIElNTkdTIGRhdGFiYXNlLCBTdWxsYW0gbGlmZXN0eWxlIGNhdGVnb3JpZXMsIGV0Yy4gYW5kIHB1dCBpdCBhbGwgaW4gb25lICAqKmVkaXRhYmxlKiogdGFibGUuIFNvIGlmIHlvdSBkaXNhZ3JlZSB3aXRoIGEgaGFiaXRhdCBwcmVkaWN0aW9uLCB5b3UgY2FuIGZlZWwgZnJlZSB0byBjaGFuZ2UgaXQuIAoKPiBEZXNjcmlwdGlvbiBvZiB0YWJsZSBoZWFkaW5ncwoKKiAqKkFTVioqOiBBU1YgaWQuCiogKipQdXRhdGl2ZSBoYWJpdGF0Kio6IE91ciBoYWJpdGF0IGRlc2lnbmF0aW9uIGJhc2VkIG9uIHRoZSBhbmFseXNlcy4KKiAqKkVucmljaGVkKio6IFdoaWNoIG9mIHRoZSBmaXZlIGhlcmJpdm9yb3VzIHJlZWYgZmlzaCB0aGUgQVNWIHdhcyBlbnJpY2hlZCBpbi4KKiAqKlRheG9uKio6IFRheG9ub21pYyBjbGFzc2lmaWNhdGlvbiBvZiB0aGUgQVNWLgoqICoqY2xvc2VzdCBkYiBtYXRjaCoqOiBIb3N0IG9yIGVudmlyb25tZW50IG9mIHRvcCBoaXQuCiogKiolIGlkZW50aXR5Kio6IFBlcmNlbnQgaWRlbnRpdHkgb2YgdG9wIGhpdC4KKiAqKnN1YmplY3QgYWNjKiogQWNjZXNzaW9uIG51bWJlciBvZiB0b3AgQkxBU1QgaGl0IAoqICoqSU1OR1MgaGl0cyoqOiBOdW1iZXIgb2YgaGl0cyB0byB0aGUgSU1OR1MgZGF0YWJhc2UuIFZhbHVlIGluZGljYXRlcyB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgdGhhdCBzY29yZWQgYSBoaXQgdG8gYW4gQVNWLgoqICoqU3VsbGFtIGxpZmVzdHlsZSoqOiBMaWZlc3R5bGUgY2F0ZWdvcnkgYXMgZGVmaW5lZCBieSBTdWxsYW0gZXQuIGFsLiwgMjAxMi4gKipOTEMqKiBpbmRpY2F0ZXMgbm8gaGl0IHRvIHRoZSBTdWxsYW0gZGIuCiogKipudW0gcGVyZmVjdCBoaXRzKiogTnVtYmVyIG9mIDEwMCUgQkxBU1QgbWF0Y2hlcyBvdXQgb2YgNTAgdG9wIGhpdHMuCgoqKk5vdGUqKiAqVGhpcyB0YWJsZSBhbHNvIHNjcm9sbHMgaG9yaXpvbnRhbGx5LiogCgpgYGB7ciBoYWJpdGF0X3RhYmxlfQpoYWJpX3RhYiA8LSByZWFkLnRhYmxlKCJNQU5VQUxfSU5QVVQvaGFiaXRhdF9zcGVjaWZpY2l0eS50eHQiLCBoZWFkZXIgPSBUUlVFLCAKICAgIHNlcCA9ICJcdCIsIGNoZWNrLm5hbWVzID0gRkFMU0UpCmhhYmlfdGFiMiA8LSBoYWJpX3RhYltvcmRlcihoYWJpX3RhYiRoYWJpdGF0X2NvZGUsIGhhYmlfdGFiJEVucmljaGVkKSwgXSAjIG9yZGVyIGJ5IGhhYml0YXQgYW5kIGhvc3QgZW5yaWNoZWQKI2hhYmlfdGFiIDwtIGhhYmlfdGFiWywgLTJdICNkZWxldGUgY29kZSBjb2x1bW4KZGF0YXRhYmxlKGhhYmlfdGFiMiwgcm93bmFtZXMgPSBGQUxTRSwgY29sbmFtZXMgPSBjKAogICAgICAgICAgICAgIkFTViIsICJIYWJpdGF0IGNvZGUiLCAiUHV0YXRpdmUgaGFiaXRhdCIsICJFbnJpY2hlZCIsIAogICAgICAgICAgICAgIlRheG9uIiwgIkNsb3Nlc3QgZGIgbWF0Y2giLCAiUGVyIGlkZW50aXR5IiwgIlN1YmplY3QgYWNjIiwgCiAgICAgICAgICAgICAiSU1OR1MgaGl0cyIsICJTdWxsYW0gbGlmZXN0eWxlIiwgIk51bSBwZXJmZWN0IGhpdHMiKSwKICAgICAgICAgIGVkaXRhYmxlID0gVFJVRSwgCiAgICAgICAgICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oc3R5bGUgPSAiY2FwdGlvbi1zaWRlOiBib3R0b207IHRleHQtYWxpZ246IGxlZnQ7IiwgCiAgICAgICAgICAgICAgICAgICAgIlRhYmxlIDY6ICIsIGh0bWx0b29sczo6ZW0oIkFzc2Vzc2luZyBoYWJpdGF0IHNwZWNpZmljaXR5LiIpKSwgCiAgICAgICAgICBleHRlbnNpb25zID0gIkJ1dHRvbnMiLCAKICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGNvbHVtbkRlZnMgPSBsaXN0KGxpc3QoY2xhc3NOYW1lID0gImR0LWNlbnRlciIsIAogICAgICAgICAgdGFyZ2V0cyA9IGMoMSwgMiwgMywgNCwgNSwgNiwgNywgOCwgOSwgMTApKSksIAogICAgICAgICAgZG9tID0gIkJsZnJ0aXAiLCBwYWdlTGVuZ3RoID0gNSwgbGVuZ3RoTWVudSA9IGMoNSwgMTAsIDI1LCA2MCksIAogICAgICAgICAgYnV0dG9ucyA9IGMoImNzdiIsICJjb3B5IiksIHNjcm9sbFggPSBUUlVFLCBzY3JvbGxDb2xsYXBzZSA9IFRSVUUpKQp3cml0ZS50YWJsZShoYWJpX3RhYjIsICJSX09VVFBVVC9oYWJpdGF0X3NwZWNpZmljaXR5LnR4dCIsIHNlcCA9ICJcdCIsIAogICAgY29sLm5hbWVzID0gVFJVRSwgcm93Lm5hbWVzID0gVFJVRSwgcXVvdGUgPSBGQUxTRSwgZmlsZUVuY29kaW5nID0gIlVURi04IikKYGBgCgoqKk5SKiogSW5kaWNhdGVzIE5vdCBSZWNvcmRlZC4gRm91ciBBU1ZzIGhhZCBudW1lcm91cyBoaXRzIGF0IDEwMCUgaWRlbnRpdHkuIFdlIGRpZCBub3QgaW5jbHVkZSB0b3AgaGl0IGRhdGEgZm9yIHRoZXNlIEFTVnMuCgoqKioKCiMjIyMgU3VtbWFyeSBvZiBoYWJpdGF0IGRpdmVyc2l0eQoKTm93IHdlIGNhbiBzdW1tYXJpemUgdGhlIGRhdGEgZm9yIGVhY2ggbGlmZXN0eWxlIGNhdGVnb3J5LiBUaGlzIHRhYmxlIHdhcyBjb25zdHJ1Y3RlZCBpbiBhIHRleHQgZmlsZSBhbmQgcmVhZCBpbnRvIFIuIAoKIyMjIzxmb250IGNvbG9yPSJyZWQiPlRhYmxlIDE8L2ZvbnQ+CgpgYGB7ciBoYWJpdGF0X3RhYmxlX3N1bW1hcnl9CmhhYmlfc3VtbWFyeSA8LSByZWFkLnRhYmxlKCJNQU5VQUxfSU5QVVQvVGFibGVfMS50eHQiLCBoZWFkZXIgPSBUUlVFLCAKICAgIHNlcCA9ICJcdCIsIGNoZWNrLm5hbWVzID0gRkFMU0UpCmRhdGF0YWJsZShoYWJpX3N1bW1hcnksIHJvd25hbWVzID0gRkFMU0UsIGVkaXRhYmxlID0gVFJVRSwgCiAgICAgICAgICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oc3R5bGUgPSAiY2FwdGlvbi1zaWRlOiBib3R0b207IHRleHQtYWxpZ246IGxlZnQ7IiwgCiAgICAgICAgICAgICAgICAgICAgIlRhYmxlIDE6ICIsIGh0bWx0b29sczo6ZW0oIlN1bW1hcnkgb2YgaGFiaXRhdCBzcGVjaWZpY2l0eS4iKSksIAogICAgICAgICAgZXh0ZW5zaW9ucyA9ICJCdXR0b25zIiwgCiAgICAgICAgICBvcHRpb25zID0gbGlzdChjb2x1bW5EZWZzID0gbGlzdChsaXN0KGNsYXNzTmFtZSA9ICJkdC1jZW50ZXIiLCAKICAgICAgICAgIHRhcmdldHMgPSBjKDEsIDIsIDMsIDQsIDUpKSksIAogICAgICAgICAgZG9tID0gIkJydGkiLCAKICAgICAgICAgIGJ1dHRvbnMgPSBjKCJjc3YiLCAiY29weSIpLCBzY3JvbGxYID0gVFJVRSwgc2Nyb2xsQ29sbGFwc2UgPSBUUlVFKSkKYGBgCgoqKioKClNvIHdlIGtub3cgdGhhdCBIb3N0IFggaXMgZW5yaWNoZWQgZm9yIHNvbWUgQVNWIGZyb20gdGF4YSBZLiBJcyB0aGlzIHBhcnQgb2YgYSBsYXJnZXIgcGF0dGVybiBvciBhbiBpc29sYXRlZCBjYXNlPyBGb3IgYSBnaXZlbiB0YXhvbm9taWMgZ3JvdXAgYW5kIHJhbmssIHdoYXQgcHJvcG9ydGlvbiBvZiB0b3RhbCByZWFkcyAoZnJvbSBhbGwgQVNWcykgd2VyZSBmb3VuZCBpbiBhIHBhcnRpY3VsYXIgaG9zdCBzcGVjaWVzPyAgQXQgc29tZSBwb2ludCBpdCB3b3VsZCBiZSBuaWNlIGlmIHRoaXMgd2VyZSBhbiBpbnRlcmFjdGl2ZSBzdGVwLCBidXQgZm9yIG5vdyB3ZSBtdXN0IG1vZGlmeSB0aGUgY29kZSBiZWxvdyB0byBsb29rIGF0IGRpZmZlcmVudCB0YXhhLiBUaGlzIGV4YW1wbGUgd2lsbCBsb29rIGF0IHRoZSBmYW1pbHkgKipEZXN1bGZvdmlicmlvbmFjZWFlKiogKERlbHRhcHJvdGVvcGJhY3RlcmlhKQoKIyMjIyBQcm9wb3J0aW9uIG9mIHRvdGFsIHJlYWRzIGZvciBhIGdpdmVuIHRheG9uICYgcmFuawoKYGBge3IgcHJvcG9ydGlvbl9vZl90YXhhfQpjYWxjX3RheF9wcm9wIDwtIHN1YnNldF90YXhhKG1lcmdlZEdQLCBGYW1pbHkgPT0gIkRlc3VsZm92aWJyaW9uYWNlYWUiKSAjIENoYW5nZSB0aGlzIHRvIHNlbGVjdCBkaWZmZXJlbnQgdGF4YQpjYWxjX3RheF9wcm9wCnNhbXBsZV9zdW1zX2J5X3RheGEgPC0gc2FtcGxlX3N1bXMoY2FsY190YXhfcHJvcCkKCnRvdGFsX3RheGFfcmVhZHMgPC0gc3VtKHNhbXBsZV9zdW1zX2J5X3RheGEpCnNhbXBsZV9zdW1zX2J5X3RheGEgPC0gYXMuZGF0YS5mcmFtZShzYW1wbGVfc3Vtc19ieV90YXhhKQoKc2FtcGxlX3N1bXNfYnlfdGF4YSRwcm9wb3J0aW9uIDwtIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoc2FtcGxlX3N1bXNfYnlfdGF4YSRzYW1wbGVfc3Vtc19ieV90YXhhL3RvdGFsX3RheGFfcmVhZHMpICogMTAwCmNvbG5hbWVzKHNhbXBsZV9zdW1zX2J5X3RheGEpIDwtIGMoInRvdGFsIHRheGEgcmVhZHMiLCAiUHJvcG9ydGlvbiIpCnNhbXBsZV9zdW1zX2J5X3RheGEkUHJvcG9ydGlvbiA8LSByb3VuZChzYW1wbGVfc3Vtc19ieV90YXhhJFByb3BvcnRpb24sIGRpZ2l0cyA9IDIpCnRvdGFsX3RheGFfcmVhZHMKc2FtcGxlX3N1bXNfYnlfdGF4YQpgYGAKCkdyZWF0LiBMb29rcyBsaWtlIHRoZXJlIGFyZSA3MSBEZXN1bGZvdmlicmlvbmFjZWFlIEFTVnMgYW5kIHRoZSBtYWpvcml0eSAoPiA5MCUpIG9mIHRoZSByZWFkcyBhcmUgZnJvbSAqQWNhbnRodXJ1cyouIFRoaXMgaXMgaW50ZXJlc3RpbmcuIFdlIGNhbiBkbyB0aGlzIHdpdGggYW55IHRheGEgd2Ugd2lzaC4gCgpBbHNvLCBmb3IgdGhlIHJlY29yZCwgaGVyZSBpcyB0aGUgKipwcm9wb3J0aW9uIG9mIEN5YW5vYmFjdGVyaWEqKiByZWFkcyBieSBob3N0IHNwZWNpZXMuIFdlIHVzZWQgdGhlIGNvZGUgYWJvdmUgYW5kIHRoZSBvcmlnaW5hbCBwaHlsb3NlcSBvYmplY3QgYmVmb3JlIHJlbW92aW5nIHRoZSBDeWFub2JhY3RlcmlhLiBUaGVyZSB3ZXJlIGEgdG90YWwgb2YgKioxMDc0IEFTVnMqKi4KCkhvc3Qgc3BlY2llc3x0b3RhbCB0YXhhIHJlYWRzfFByb3BvcnRpb258CnwtLS18LS0tfC0tLXwKfEFjQ29lfDExNzkwOXw1My4yN3wJCQp8QWNUcmF8NDMyNjF8MTkuNTR8CQkKfFNjVGFlfDMxMDEzfDE0LjAxfAkJCnxTcEF1cnwxNDUzNHw2LjU3fAkJCnxTcFZpcnwxNDYzNnw2LjYxfAkKCgoqKioKCkF0IHRoaXMgcG9pbnQgd2Uga25vdyB3aGljaCBBU1ZzIGFyZSBlbnJpY2hlZCBpbiB3aGljaCBob3N0IHNwZWNpZXMsIHRoZSBsaW5lYWdlIG9mIHRob3NlIEFTVnMsIGFuZCBzb21ldGhpbmcgYWJvdXQgd2hlcmUgZWxzZSB0aGVzZSBzZXF1ZW5jZXMgaGF2ZSBiZWVuIGRldGVjdGVkIGluIG5hdHVyZS4gTmV4dCB3ZSB3b3VsZCBsaWtlIHRvIGtub3cgdGhlIHByb3BvcnRpb24gb2YgdG90YWwgcmVhZHMgZm9yIGVhY2ggQVNWIHRoYXQgaXMgZm91bmQgaW4gZWFjaCBob3N0IHNwZWNpZXMuIFdlIHN0YXJ0IHdpdGggYSBzdW1tYXJ5IHRhYmxlIG9mIHRoZXNlIGRhdGEuIAoKIyMjIyBQcm9wb3J0aW9uIG9mIHRvdGFsIEFTViByZWFkcyBwZXIgaG9zdCBzcGVjaWVzCgpgYGB7ciBkYV9hc3ZfYmFyX3NldHVwLCBtZXNzYWdlID0gRkFMU0V9CiMgY2FsY3VsYXRlIHRoZSBhdmVyYWdlcyBhbmQgbWVyZ2UgYnkgc3BlY2llcwojIGdyYWIgdGhlIGRhX2FzdiBwcyBvYmplY3QgJiBtZXJnZSBieSBzYW1wbGVzCmRhQVNWX21lcmdlZEdQX0JBUiA8LSBtZXJnZV9zYW1wbGVzKGRhX2FzdnNfZnVsbCwgIlNwIikKI2RhQVNWX1NEX0JBUiA8LSBtZXJnZV9zYW1wbGVzKHNhbXBsZV9kYXRhKGRhX2FzdnNfZnVsbCksICJTcCIpCiMgY2FsY3VsYXRlIHBlcmNlbnQgcHJvcG9ydGlvbgpkYUFTVl9BVkcgPC0gYXBwbHkodChvdHVfdGFibGUoZGFBU1ZfbWVyZ2VkR1BfQkFSKSksIDEsIGZ1bmN0aW9uKHgpIHgvc3VtKHgpKSAKIyB0cmFuc3Bvc2UKZGFBU1ZfdF9BVkcgPC0gdChkYUFTVl9BVkcpCmRhQVNWX3RfQVZHX2RmIDwtIGFzLmRhdGEuZnJhbWUoZGFBU1ZfdF9BVkcpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmRhX0FTVl90YXggPC0gaGFiaV90YWJbYygiQVNWIiwgIlRheG9uIiwgIlB1dGF0aXZlX2hhYml0YXQiKV0gIyBjaG9vc2UgY29sdW1ucyBvZiBpbnRlcmVzdApkYV9BU1ZfdGF4MiA8LSBkYV9BU1ZfdGF4WywtMV0Kcm93bmFtZXMoZGFfQVNWX3RheDIpIDwtIGRhX0FTVl90YXhbLDFdCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBjb21iaW5lIGJhc2VkIG9uIEFTViBjb2x1bW4KZGFBU1Zfd29yayA8LSBtZXJnZShkYUFTVl90X0FWR19kZiwgZGFfQVNWX3RheDIsIGJ5ID0gMCwgYWxsID0gVFJVRSkKcm93bmFtZXMoZGFBU1Zfd29yaykgPC0gZGFBU1Zfd29ya1ssMV0KZGFBU1Zfd29yayRSb3cubmFtZXMgPC0gTlVMTAojIHRoZW4gbWFrZSBjb2x1bW4gcm93Lm5hbWVzCmRhQVNWX3dvcmsyIDwtIGNiaW5kKEFTViA9IHJvd25hbWVzKGRhQVNWX3dvcmspLCBkYUFTVl93b3JrKQojbWVsdCB0aGUgZGYKZGFBU1Zfd29yazMgPC0gbWVsdChkYUFTVl93b3JrMiwgdmFsdWUubmFtZSA9ICJBU1YiKSAjIHdpZGUgdG8gbG9uZyBmb3JtYXQ/CmNvbG5hbWVzKGRhQVNWX3dvcmszKSA8LSBjKCJBU1YiLCAiVGF4b24iLCAiUHV0YXRpdmVfaGFiaXRhdCIsICJTYW1wbGUiLCAiUHJvcG9ydGlvbiIpCmRhQVNWX3dvcmszJFByb3BvcnRpb24gPC0gcm91bmQoZGFBU1Zfd29yazMkUHJvcG9ydGlvbiwgZGlnaXRzID0gNSkKCmRhdGF0YWJsZShkYUFTVl93b3JrMywgcm93bmFtZXMgPSBUUlVFLCBlZGl0YWJsZSA9IEZBTFNFLCAKICAgICAgICAgIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbihzdHlsZSA9ICJjYXB0aW9uLXNpZGU6IGJvdHRvbTsgdGV4dC1hbGlnbjogbGVmdDsiLCAKICAgICAgICAgICAgICAgICAgICAiVGFibGUgODogIiwgaHRtbHRvb2xzOjplbSgiREEgQVNWIHNhbXBsZSBwcm9wb3J0aW9uLiIpKSwgCiAgICAgICAgICBleHRlbnNpb25zID0gIkJ1dHRvbnMiLCAKICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGNvbHVtbkRlZnMgPSBsaXN0KGxpc3QoY2xhc3NOYW1lID0gImR0LWNlbnRlciIsIAogICAgICAgICAgdGFyZ2V0cyA9IGMoMSwgMiwgMywgNCwgNSkpKSwgCiAgICAgICAgICBkb20gPSAiQmxmcnRpcCIsIHBhZ2VMZW5ndGggPSA1LCBsZW5ndGhNZW51ID0gYyg1LDEwLCA1MCwgMTAwLCAzMDApLCAKICAgICAgICAgIGJ1dHRvbnMgPSBjKCJjc3YiLCAiY29weSIpLCBzY3JvbGxYID0gVFJVRSwgc2Nyb2xsQ29sbGFwc2UgPSBUUlVFKSkKCiN3cml0ZS50YWJsZShkYUFTVl93b3JrMywgIlJfT1VUUFVUL1RhYmxlXzE4LnR4dCIsIAojICAgICAgICAgICAgc2VwID0gIlx0Iiwgcm93Lm5hbWVzID0gRkFMU0UsIHF1b3RlID0gRkFMU0UpCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBhIGxpc3Qgb2YgREEgQVNWcyBhbmQgdGhlaXIgYXNzaWduZWQgIGhhYml0YXQgcHJlZmVyZW5jZSwgd2Ugd2FudCB0byBjcmVhdGUgYSBSIG9iamVjdCB0aGF0IG9yZ2FuaXplcyB0aGVzZSBpbiBzb21lIGxvZ2ljYWwgZmFzaGlvbi4gV2UgY2FuIHRoZW4gdXNlIHRoaXMgb2JqZWN0IHRvIG9yZGVyIHN1YnNlcXVlbnQgZ3JhcGhzIGFuZCB0YWJsZXMuIFNvIGxldHMgb3JkZXIgdGhlIEFTVnMgYnkgcHV0YXRpdmUgaGFiaXRhdCBwcmVmZXJlbmNlIGFuZCB0aGVuIGJ5IHRoZSBob3N0IHNwZWNpZXMgaW4gd2hpY2ggdGhhdCBBU1Ygd2FzIGVucmljaGVkLiBTZWVtcyByZWFzb25hYmxlIGVub3VnaD8gRGVwZW5kaW5nIG9uIHRoZSBSIGNvbW1hbmQsIHNvbWUgIG9iamVjdHMgbmVlZCB0byBiZSBpbiBhc2NlbmRpbmcgb3JkZXIsIG90aGVycyBpbiBkZXNjZW5kaW5nIG9yZGVyLgoKYGBge3Igc2V0X2xlZnNlX29yZGVyX2FzdnN9CmFzdl9vcmRlciA8LSBjKCJBU1Y0NTAiLCAiQVNWMTY1IiwgIkFTVjM5NSIsICJBU1YyODQiLCAiQVNWNTYiLCAiQVNWNiIsIAogICAgICAgICAgICAgICAiQVNWMzU5IiwgIkFTVjEyOCIsICJBU1YxMjciLCAiQVNWOTEiLCAiQVNWMzc0IiwgIkFTVjE1MSIsIAogICAgICAgICAgICAgICAiQVNWMzIzIiwgIkFTVjM5OCIsICJBU1YyMjQiLCAiQVNWMzkiLCAiQVNWMzQiLCAiQVNWMTIiLCAKICAgICAgICAgICAgICAgIkFTVjMyIiwgIkFTVjI1MCIsICJBU1Y0MyIsICJBU1Y1NCIsICJBU1Y5IiwgIkFTVjUiLCAiQVNWNDkiLCAKICAgICAgICAgICAgICAgIkFTVjgiLCAiQVNWNDEiLCAiQVNWMTgiLCAiQVNWNyIsICJBU1Y5MCIsICJBU1YyOSIsICJBU1Y5OCIsIAogICAgICAgICAgICAgICAiQVNWMjMiLCAiQVNWMzAiLCAiQVNWMjI2IiwgIkFTVjQ4IiwgIkFTVjcwIiwgIkFTVjEiLCAiQVNWMTQiLCAKICAgICAgICAgICAgICAgIkFTVjI5OCIsICJBU1Y4MiIsICJBU1Y3NSIsICJBU1Y2OSIsICJBU1Y1NyIsICJBU1YyMCIsICJBU1YxNSIsIAogICAgICAgICAgICAgICAiQVNWMiIsICJBU1YyNjgiLCAiQVNWMTE0IiwgIkFTVjIzNCIsICJBU1YxNzQiLCAiQVNWNjAiLCAiQVNWMTciLCAKICAgICAgICAgICAgICAgIkFTVjIyIiwgIkFTVjE1OSIsICJBU1Y0NCIsICJBU1YyNSIsICJBU1YyMSIsICJBU1YzNSIpCmFzdl9vcmRlcl9yZXYgPC0gcmV2KGFzdl9vcmRlcikKYGBgCgpMZXRzIHNlZSBpZiB3ZSBjYW4gb3ZlcmxheSBhbGwgb2YgdGhpcyBpbmZvcm1hdGlvbiBpbiBvbmUgImVhc3kiIHRvIHVuZGVyc3RhbmQgcGxvdC4gVGhlIGZpcnN0IHRoaW5nIGlzIHRvIGRvIGlzIHBsb3QgdGhlIHByb3BvcnRpb24gb2YgcmVhZHMgZm9yIGEgZ2l2ZW4gQVNWIGZyb20gZWFjaCBob3N0IHNwZWNpZXMuCgojIyMjIEdyYXBoaWNhbCByZXByZXNlbnRhdGlvbiBvZiBwcm9wb3J0aW9uIG9mIHRvdGFsIEFTViByZWFkcyBwZXIgaG9zdCBzcGVjaWVzCgoKYGBge3Igb3JkZXJfYXN2c30KZGFBU1Zfd29yazMkQVNWIDwtIGFzLmNoYXJhY3RlcihkYUFTVl93b3JrMyRBU1YpCmRhQVNWX3dvcmszJEFTViA8LSBmYWN0b3IoZGFBU1Zfd29yazMkQVNWLCBsZXZlbHMgPSB1bmlxdWUoZGFBU1Zfd29yazMkQVNWKSkKZGFBU1Zfd29yazMkQVNWIDwtIGZhY3RvcihkYUFTVl93b3JrMyRBU1YsIGxldmVscz0gYXN2X29yZGVyKQpgYGAKCk5leHQsIHdlIGNyZWF0ZWQgYSBiYXIgcGxvdCBvZiByZWFkIHByb3BvcnRpb24gYnkgaG9zdCBzcGVjaWVzIGZvciBlYWNoIEFTVi4gQW5kIHNhdmUgYSBjb3B5IHRvIHRoZSBgRklHVVJFUy9gIGRpcmVjdG9yeS4KCkNvZGUgZm9yIHByb3BvcnRpb25hbCBiYXIgY2hhcnQKCmBgYHtyIGRhX2Fzdl9iYXJfY2hhcnQsIG1lc3NhZ2UgPSBGQUxTRX0KI0JhciBjaGFydHMKQVNWX2JhciA8LSBnZ3Bsb3QoZGFBU1Zfd29yazMsIGFlc19zdHJpbmcoeCA9ICJBU1YiLCAKICAgICAgeSA9ICJQcm9wb3J0aW9uIiwgZmlsbCA9ICJTYW1wbGUiKSwgZW52aXJvbm1lbnQgPSAuZSwgb3JkZXJlZCA9IFRSVUUsIHhsYWIgPSAieC1heGlzIGxhYmVsIiwgCiAgICAgIHlsYWIgPSAieS1heGlzIGxhYmVsIikgCkFTVl9iYXIgPC0gQVNWX2JhciArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHJldmVyc2UgPSBUUlVFKSwgCiAgICB3aWR0aCA9IDAuOTUpICsgY29vcmRfZmxpcCgpICsgdGhlbWUoYXNwZWN0LnJhdGlvID0gMi8xKQpBU1ZfYmFyIDwtIEFTVl9iYXIgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBzYW1wX3BhbCkKQVNWX2JhciA8LSBBU1ZfYmFyICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDAuOTUsIAogICAgdmp1c3QgPSAxKSkKQVNWX2JhciA8LSBBU1ZfYmFyICsgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChjb2xvdXIgPSBOVUxMKSwgCiAgICByZXZlcnNlID0gRkFMU0UpKSArIHRoZW1lKGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIikpCkFTVl9iYXIgPC0gQVNWX2JhciArIGxhYnMoeCA9ICJIb3N0IHNwZWNpZXMiLCB5ID0gIlByb3BvcnRpb24gKCUgdG90YWwgcmVhZHMpIiwgCiAgICB0aXRsZSA9ICJBU1YgUHJvcG9ydGlvbiBieSBob3N0IHNwZWNpZXMiKQpBU1ZfYmFyIDwtIEFTVl9iYXIgKyB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIAogICAgICAgIGZpbGwgPSBOQSwgc2l6ZSA9IDEpKQpBU1ZfYmFyCnBkZigiUl9PVVRQVVQvYmFyX3Bsb3RfcmVhZF9wcm9wLnBkZiIpCkFTVl9iYXIKaW52aXNpYmxlKGRldi5vZmYoKSkKYGBgCgpDb2RlIGZvciBoZWF0bWFwCgpgYGB7ciBoZWF0bWFwX2FsdF9zZXR1cCwgd2FybmluZz1GQUxTRSwgIG1lc3NhZ2UgPSBGQUxTRX0KIyBIZWF0bWFwCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoY2lyY2xpemUpCmxpYnJhcnkoaGVhdG1hcDMpCmxpYnJhcnkoZ2RhdGEpCmZpZzRfaGVhdCA8LSBhcy5kYXRhLmZyYW1lKHQob3R1X3RhYmxlKGRhX2FzdnMpKSkKZmlnNF90YXggPC0gYXMuZGF0YS5mcmFtZShoYWJpX3RhYjIpICMgQ29udmVydCBoYWJpX3RhYmxlIHRvIGRmIGFuZCBzdG9yZSBpbiBuZXcgdmFyaWFibGUKZmlnNF90YXhfdGFiIDwtIGZpZzRfdGF4WywgLTFdICMgZWxpbWluYXRlIDFzdCBjb2x1bW4gc28gY2FuIGNvbWJpbmUgYmFzZWQgb24gcm93IG5hbWVzCnJvd25hbWVzKGZpZzRfdGF4X3RhYikgPC0gZmlnNF90YXhbLCAxXSAjIE1ha2UgbmV3IHJvdy5uYW1lcyBmcm9tIG9yaWdpbmFsIHRhYmxlCmZpZzRfdGF4X3RhYiA8LSBmaWc0X3RheF90YWJbYyg0LDIsMywxLDUsNiw3LDgpXSAjIFJlb3JkZXIKZmlnNF90YXhfdGFiIDwtIGZpZzRfdGF4X3RhYltjKDE6NCldICMgU2VsZWN0IGNvbHVtbnMKCiMgQ29tYmluZSB0aGUgdHdvIGRmIGJ5IHJvd25hbWUKIyBJZiB0aGUgbWF0Y2hpbmcgaW52b2x2ZWQgcm93IG5hbWVzLCBhbiBleHRyYSBjaGFyYWN0ZXIgY29sdW1uIGNhbGxlZCBSb3cubmFtZXMgCiNpcyBhZGRlZCBhdCB0aGUgbGVmdCwgYW5kIGluIGFsbCBjYXNlcyB0aGUgcmVzdWx0IGhhcyDigJhhdXRvbWF0aWPigJkgcm93IG5hbWVzLgpmaWc0X2hlYXRtYXAyIDwtIG1lcmdlKGZpZzRfdGF4X3RhYiwgZmlnNF9oZWF0LCBieSA9IDAsIGFsbCA9IFRSVUUpIAoKZmlnNF9oZWF0bWFwIDwtIHN1YnNldChmaWc0X2hlYXRtYXAyLCBzZWxlY3Q9LWMoUm93Lm5hbWVzKSkKcm93bmFtZXMoZmlnNF9oZWF0bWFwKSA8LSBmaWc0X2hlYXRtYXAyWywnUm93Lm5hbWVzJ10KZmlnNF9oZWF0bWFwIDwtIGNiaW5kKEFTViA9IHJvd25hbWVzKGZpZzRfaGVhdG1hcCksIGZpZzRfaGVhdG1hcCkgIyBtYWtlIHJvd25hbWVzIGEgY29sdW1uCgpmaWc0X2hlYXRtYXAkQVNWIDwtIGZhY3RvcihmaWc0X2hlYXRtYXAkQVNWLCBsZXZlbHM9cmV2KGFzdl9vcmRlcikpIApmaWc0X2hlYXRtYXAgPC0gZmlnNF9oZWF0bWFwW29yZGVyKGZpZzRfaGVhdG1hcCRBU1YpLF0gCgpmaWc0X2hlYXRtYXAkSUQgPC0gcGFzdGUoZmlnNF9oZWF0bWFwJEFTViwgZmlnNF9oZWF0bWFwJFRheG9uLCBmaWc0X2hlYXRtYXAkUHV0YXRpdmVfaGFiaXRhdCwgZmlnNF9oZWF0bWFwJEVucmljaGVkLCBzZXAgPSAiXyIpICMgY29tYmluZSB0aGUgY29sdW1ucyB0byBtYWtlIG9uZSBuYW1lCmZpZzRfaGVhdG1hcDIgPC0gZmlnNF9oZWF0bWFwWy1jKDE6NSldICMgZGVsZXRlIHRoZSBvcmlnaW5hbCBjb2x1bW5zCmZpZzRfaGVhdG1hcDIgPC0gZmlnNF9oZWF0bWFwMltjKDYsMSwyLDMsNCw1KV0gIyByZW9yZGVyCnJvd25hbWVzKGZpZzRfaGVhdG1hcDIpIDwtIGZpZzRfaGVhdG1hcDJbLCAxXQpmaWc0X2hlYXRtYXAyIDwtIGZpZzRfaGVhdG1hcDJbLTFdCmBgYAoKIyMjIzxmb250IGNvbG9yPSJyZWQiPkZpZ3VyZSA0PC9mb250PgoKYGBge3IgbWFrZV9wbG90fQoKIyMjI0RlZmluZSBDb2xvcnMKdGF4YV9jb2xvcnMgPC0gdW5saXN0KGxhcHBseShyb3cubmFtZXMoZmlnNF9oZWF0bWFwMiksIGZ1bmN0aW9uKHgpewogIGlmKGdyZXBsCiAgICAgCiAgICAgIyBlbnZpcm9ubWVudGFsCiAgICAgICAgICAgICAgICgiQWxwaGFwcm90ZW9iYWN0ZXJpYSIsIHgpKSAnIzAwMDAwMCcgCiAgZWxzZSBpZihncmVwbCgiUGlyZWxsdWxhY2VhZSIsIHgpKSAnIzAwMDAwMCcgCiAgZWxzZSBpZihncmVwbCgiUnVicml0YWxlYWNlYWUiLCB4KSkgJyMwMDAwMDAnIAogIGVsc2UgaWYoZ3JlcGwoIkZsYXZvYmFjdGVyaWFjZWFlIiwgeCkpICcjMDAwMDAwJyAKICAgIAogICAjIGZpc2gvYW5pbWFsCiAgZWxzZSBpZihncmVwbCgiRGVzdWxmb3ZpYnJpb25hY2VhZSIsIHgpKSAnIzAwNzJiMicgCiAgZWxzZSBpZihncmVwbCgiTGFjaG5vc3BpcmFjZWFlIiwgeCkpICcjZjBlNDQyJyAKICBlbHNlIGlmKGdyZXBsKCJFcnlzaXBlbG90cmljaGFjZWFlIiwgeCkpICcjMDA5ZTczJyAKICBlbHNlIGlmKGdyZXBsKCJSdW1pbm9jb2NjYWNlYWUiLCB4KSkgJyNlNjlmMDAnCiAgZWxzZSBpZihncmVwbCgiQmFjdGVyb2lkYWxlcyIsIHgpKSAnI2Q1NWUwMCcKICBlbHNlIGlmKGdyZXBsKCJGdXNvYmFjdGVyaWFjZWFlIiwgeCkpICcjNTZiNGU5JwoKICAjIFVuZGVydGVybWluZWQKICBlbHNlIGlmKGdyZXBsKCJWaWJyaW9uYWNlYWUiLCB4KSkgJyNjYzc5YTcnCiAgCiAgIyBvdGhlcgogIGVsc2UgaWYoZ3JlcGwoIkZhbWlseV9YSUlJIiwgeCkpICcjODA4MDgwJwogIGVsc2UgaWYoZ3JlcGwoIk1vbGxpY3V0ZXMiLCB4KSkgJyM4MDgwODAnIAogIGVsc2UgaWYoZ3JlcGwoIkJyZXZpbmVtYXRhY2VhZSIsIHgpKSAnIzgwODA4MCcKICBlbHNlIGlmKGdyZXBsKCJQZXB0b3N0cmVwdG9jb2NjYWNlYWUiLCB4KSkgJyM4MDgwODAnCn0pKSAgICAKCmhhYml0YXRfY29sb3JzIDwtIHVubGlzdChsYXBwbHkocm93Lm5hbWVzKGZpZzRfaGVhdG1hcDIpLCBmdW5jdGlvbih4KXsKICBpZihncmVwbAogICAgICAgICAgICAgICAoImZpc2giLCB4KSkgJyM4MDgwODAnCiAgZWxzZSBpZihncmVwbCgiYW5pbWFsIiwgeCkpICcjMDAwMDAwJwogIGVsc2UgaWYoZ3JlcGwoImVudmlyb25tZW50YWwiLCB4KSkgJyM4MDgwODAnCiAgZWxzZSBpZihncmVwbCgidW5kZXRlcm1pbmVkIiwgeCkpICcjMDAwMDAwJwp9KSkgICAgCmhlYXRDb2xvcnMgPC0gY2JpbmQodGF4YV9jb2xvcnMsIGhhYml0YXRfY29sb3JzKQpjb2xuYW1lcyhoZWF0Q29sb3JzKVsxXSA8LSAiVGF4YSIKY29sbmFtZXMoaGVhdENvbG9ycylbMl0gPC0gIkhhYml0YXQiCmNvbCA8LSBjb2xvclJhbXBQYWxldHRlKGJpYXMgPSAxLCBjKCIjMDAwMDMzIiwgIiM2NkNDRkYiKSkoMTYpCgojIyMgU2F2ZSBoZWF0bWFwCnBkZihmaWxlID0gIlJfT1VUUFVUL0ZpZ3VyZV80LnBkZiIpCmhlYXRtYXAzKGZpZzRfaGVhdG1hcDIsIGNleFJvdz0wLjUsIGNleENvbD0xLCAKICAgICAgICAgbWFyZ2lucyA9IGMoMywxMyksIFJvd1NpZGVDb2xvcnM9aGVhdENvbG9ycywgc2NhbGU9InJvdyIsIENvbHYgPSBOQSwgCiAgICAgICAgIFJvd3YgPSBOQSwgcmV2QyA9IFRSVUUsIGJhbGFuY2VDb2xvciA9IEZBTFNFLCBjb2wgPSBjb2wpCmludmlzaWJsZShkZXYub2ZmKCkpCmhlYXRtYXAzKGZpZzRfaGVhdG1hcDIsY2V4Um93PTAuNSwgY2V4Q29sPTEsIAogICAgICAgICBtYXJnaW5zID0gYygzLDEzKSwgUm93U2lkZUNvbG9ycz1oZWF0Q29sb3JzLCBzY2FsZT0icm93IiwgQ29sdiA9IE5BLCAKICAgICAgICAgUm93diA9IE5BLCByZXZDID0gVFJVRSwgYmFsYW5jZUNvbG9yID0gRkFMU0UsIGNvbCA9IGNvbCkKYGBgCgoKCioqKgoKPGEgaWQ9IkFwcGVuZGl4IEE6IE90aGVyIGFuYWx5c2VzICYgdmlzdWFsaXphdGlvbnMiPjwvYT4KCiMjQXBwZW5kaXggQTogT3RoZXIgYW5hbHlzZXMgJiB2aXN1YWxpemF0aW9ucwoKW2JhY2sgdG8gdG9wXSgjYmFjayB0byB0b3ApCgpIZXJlIGlzIGNvZGUgZm9yIG90aGVyIHJlcHJlc2VudGF0aW9uIG9mIHRheGEgYWJ1bmRhbmNlLiBUaGVzZSBhcmUgcmF3IGBSYCBpbWFnZXMgdGhhdCBoYXZlIG5vdCBiZWVuIGd1c3NpZWQgdXAuCgpXZSB3aWxsIGNyZWF0ZSB0d28gZGlmZmVyZW50IHJlcHJlc2VudGF0aW9ucyBvZiByZWxhdGl2ZSBhYnVuZGFuY2UgZm9yIGVhY2ggc2FtcGxlIChhcnJhbmdlZCBieSBob3N0IHNwZWNpZXMpIGJ5IG1ham9yIENsYXNzZXMtLS1mYWNldCBncmlkIGJveC1hbmQtd2hpc2tlciBwbG90cyBhbmQgYmFyIGNoYXJ0cy4gV2Ugd2lsbCBnZW5lcmF0ZSBlYWNoIHNlcGFyYXRlbHkgKGFuZCBzYXZlKSB1c2luZyBhbiBlYXJsaWVyICByZWxhdGl2ZSBhYnVuZGFuY2UgcGh5bG9zZXEgb2JqZWN0IGFuZCB0aGVuIGRpc3BsYXkgdGhlIGNvbWJpbmVkIG91dHB1dC4gCgoqKkNvZGUgZm9yIGJveC1hbmQtd2hpc2tlciBwbG90LioqCgpgYGB7ciBzdXBwX2ZpZzFfY2FsYywgcmVzdWx0cyA9ICJoaWRlIn0KbWRhdGFfcGh5X2FsbCA8LSB0YXhfZ2xvbShwc19zbHZfZmlsdF9BVkcsIHRheHJhbmsgPSAiQ2xhc3MiLCBOQXJtID0gRkFMU0UpCiMgWW91IGNhbiBjaG9vc2UgYW55IHRheG9ub21pYyBsZXZlbCBoZXJlCm1kYXRhX3BoeXJlbF9hbGwgPC0gdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMobWRhdGFfcGh5X2FsbCwgZnVuY3Rpb24oeCkgeC9zdW0oeCkpCm1lbHRkX2FsbCA8LSBwc21lbHQobWRhdGFfcGh5cmVsX2FsbCkKbWVsdGRfYWxsJENsYXNzIDwtIGFzLmNoYXJhY3RlcihtZWx0ZF9hbGwkQ2xhc3MpCgptZWFucyA8LSBkZHBseShtZWx0ZF9hbGwsIH5DbGFzcywgZnVuY3Rpb24oeCkgYyhtZWFuID0gbWVhbih4JEFidW5kYW5jZSkpKQp0YXhhX21lYW5zIDwtIG1lYW5zW29yZGVyKC1tZWFucyRtZWFuKSwgXSAgIyBkZWNlbmRpbmcgb3JkZXIKdGF4YV9tZWFucyA8LSBmb3JtYXQodGF4YV9tZWFucywgc2NpZW50aWZpYyA9IEZBTFNFKSAgIyBkaXRjaCB0aGUgc2NpIG5vdGF0aW9uIAoKT3RoZXIgPC0gbWVhbnNbbWVhbnMkbWVhbiA8PSAwLjAyNywgXSRDbGFzcyAgIyBIZXJlIHdlIGNvbmdsb21lcmF0ZSBhdCAyJS4KCm1lbHRkX2FsbFttZWx0ZF9hbGwkQ2xhc3MgJWluJSBPdGhlciwgXSRDbGFzcyA8LSAiT3RoZXIiCnNhbXBfbmFtZXMgPC0gYWdncmVnYXRlKG1lbHRkX2FsbCRBYnVuZGFuY2UsIGJ5ID0gbGlzdChtZWx0ZF9hbGwkU2FtcGxlKSwgCiAgICBGVU4gPSBzdW0pWywgMV0KLmUgPC0gZW52aXJvbm1lbnQoKQptZWx0ZF9hbGxbLCAiQ2xhc3MiXSA8LSBmYWN0b3IobWVsdGRfYWxsWywgIkNsYXNzIl0sIHNvcnQodW5pcXVlKG1lbHRkX2FsbFssIAogICAgIkNsYXNzIl0pKSkKbWVsdGRfYWxsIDwtIG1lbHRkX2FsbFtvcmRlcihtZWx0ZF9hbGxbLCAiQ2xhc3MiXSksIF0KbGV2ZWxzKG1lbHRkX2FsbCRDbGFzcykKbWVsdGRfYWxsJENsYXNzIDwtIGZhY3RvcihtZWx0ZF9hbGwkQ2xhc3MsIGxldmVscyA9IGMoIkJhY3Rlcm9pZGlhIiwgIkNsb3N0cmlkaWEiLCAKICAgICJFcnlzaXBlbG90cmljaGlhIiwgIkZ1c29iYWN0ZXJpaWEiLCAiQWxwaGFwcm90ZW9iYWN0ZXJpYSIsICJEZWx0YXByb3Rlb2JhY3RlcmlhIiwgCiAgICAiR2FtbWFwcm90ZW9iYWN0ZXJpYSIsICJQbGFuY3RvbXljZXRhY2lhIiwgIk90aGVyIikpICAjIEhlcmUgd2Ugb3JkZXIgQ2xhc3NlcyBieSB0aGUgUGh5bHVtIHRoZXkgYmVsb25nIHRvLgoKc3VwX2ZpZzEgPC0gcXBsb3QoZGF0YSA9IG1lbHRkX2FsbCwgeCA9IFNwLCB5ID0gQWJ1bmRhbmNlLCBmaWxsID0gQ2xhc3MsIAogICAgZ2VvbSA9ICJib3hwbG90IiwgeWxhYiA9ICJSZWxhdGl2ZSBBYnVuZGFuY2UiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArIAogICAgZmFjZXRfZ3JpZChDbGFzcyB+IC4sIHNjYWxlcyA9ICJmcmVlX3kiLCBzcGFjZSA9ICJmcmVlX3kiKSArIGdlb21faml0dGVyKHdpZHRoID0gMC4wNSkgKyAKICAgIGdlb21fcG9pbnQoY29sb3VyID0gImJsYWNrIiwgZmlsbCA9ICJ3aGl0ZSIpICAjKyBndWlkZXMoZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBGQUxTRSkgKQpzdXBfZmlnMSA8LSBzdXBfZmlnMSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGZyaWVuZF9wYWwpICsgbGFicyh4ID0gIkhvc3Qgc3BlY2llcyIsIAogICAgeSA9ICJSZWxhdGl2ZSBhYnVuZGFuY2UgKCUgdG90YWwgcmVhZHMpIikKc3VwX2ZpZzEKcGRmKCJSX09VVFBVVC9ib3gtYW5kLXdoaXNrZXIucGRmIikKc3VwX2ZpZzEKaW52aXNpYmxlKGRldi5vZmYoKSkKYGBgCgoqKkNvZGUgZm9yIGJhciBwbG90LioqCgojIyMjPGZvbnQgY29sb3I9InJlZCI+U3VwcGxlbWVudGFyeSBGaWd1cmUgMTwvZm9udD4KCmBgYHtyIHNlcGFyYXRlX2JhcnNfc3RhY2tlZCwgZmlnLmFsaWduID0gImNlbnRlciIsIGZpZy5jYXAgPSAiU3VwcGxlbWVudGFyeSBGaWd1cmUgMS4gUmVsYXRpdmUgYWJ1bmRhbmNlIG9mIG1ham9yIENsYXNzZXMgYnkgc2FtcGxlIiwgb3V0LndpZHRoID0gIjkwJSJ9CnN1cF9maWcyIDwtIGdncGxvdChtZWx0ZF9hbGwsIGFlc19zdHJpbmcoeCA9ICJTYW1wbGUiLCB5ID0gIkFidW5kYW5jZSIsIAogICAgZmlsbCA9ICJDbGFzcyIpLCBlbnZpcm9ubWVudCA9IC5lLCBPcmRlcmVkID0gVFJVRSkKc3VwX2ZpZzIgPC0gc3VwX2ZpZzIgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAic3RhY2siKSArIAogICAgZmFjZXRfZ3JpZChDbGFzcyB+IFNwLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKQpzdXBfZmlnMiA8LSBzdXBfZmlnMiArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGZyaWVuZF9wYWwpCgojIHN1cF9maWcyIDwtIHN1cF9maWcyICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAtOTAsCiMgaGp1c3QgPSAwKSkKc3VwX2ZpZzIgPC0gc3VwX2ZpZzIgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkKc3VwX2ZpZzIgPC0gc3VwX2ZpZzIgKyBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGNvbG91ciA9IE5VTEwpLCAKICAgIHJldmVyc2UgPSBGQUxTRSkpICsgdGhlbWUobGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArIAogICAgbGFicyh4ID0gIkluZGl2aWR1YWwgc2FtcGxlcyIsIHkgPSAiUmVsYXRpdmUgYWJ1bmRhbmNlICglIHRvdGFsIHJlYWRzKSIpCnN1cF9maWcyCnBkZigiUl9PVVRQVVQvU3VwcGxlbWVudGFyeV9GaWd1cmVfMS5wZGYiKQpzdXBfZmlnMgppbnZpc2libGUoZGV2Lm9mZigpKQpgYGAKCgpbcmV0dXJuIHRvIEZpZ3VyZSAyQV0oI3JlbGF0aXZlIGFidW5kYW5jZSBwaHlsb3NlcSBvYmplY3QpCgojI0FwcGVuZGl4IEI6IFRvb2xzICYgcmVzb3VyY2VzIHVzZWQgaW4gdGhpcyB3b3JrZmxvdyAKCltiYWNrIHRvIHRvcF0oI2JhY2sgdG8gdG9wKQoKKipTcGVjaWZpYyB0b29scyoqCgotLSBbcGh5bG9zZXFdKGh0dHBzOi8vam9leTcxMS5naXRodWIuaW8vcGh5bG9zZXEvKXt0YXJnZXQ9Il9ibGFuayJ9IGFzIHRoZSBwcmltYXJ5IGFuYWx5dGljYWwgcGFja2FnZS4gIAotLSBbTEVmU2VdKGh0dHBzOi8vZHguZG9pLm9yZy8xMC4xMDM4JTJGbm1ldGguMjY1OCl7dGFyZ2V0PSJfYmxhbmsifSB0byBpZGVudGlmeSBkaWZmZXJlbnRpYWxseSBhYnVuZGFudCAoREEpIGFtcGxpY29uIHNlcXVlbmNlIHZhcmlhbnRzIChBU1YpIGFjcm9zcyBob3N0IGZpc2ggc3BlY2llcy4gIAotLSBbTWljcm9iaW9tZUFuYWx5c3RdKGh0dHA6Ly93d3cubWljcm9iaW9tZWFuYWx5c3QuY2EvKXt0YXJnZXQ9Il9ibGFuayJ9ICB0byBjb25kdWN0IExFZlNlIGFuYWx5c2lzLiAgCi0tIFtCTEFTVG5dKGh0dHBzOi8vYmxhc3QubmNiaS5ubG0ubmloLmdvdi9CbGFzdC5jZ2kpe3RhcmdldD0iX2JsYW5rIn0gYW5kIFtTaWx2YSBBQ1RdKGh0dHBzOi8vd3d3LmFyYi1zaWx2YS5kZS9hbGlnbmVyLyl7dGFyZ2V0PSJfYmxhbmsifSB0byBpZGVudGlmeSBjbG9zZXN0IGhpdHMgdG8gREEgQVNWcy4gIAotLSBbUkF4TUxdKGh0dHBzOi8vc2NvLmgtaXRzLm9yZy9leGVsaXhpcy93ZWIvc29mdHdhcmUvcmF4bWwvKXt0YXJnZXQ9Il9ibGFuayJ9IGZvciBwaHlsb2dlbmV0aWMgaW5mZXJlbmNlIG9mIERBIEFTVnMgYW5kIGNsb3Nlc3QgaGl0cy4gIAotLSBbaVRPTF0oaHR0cHM6Ly9pdG9sLmVtYmwuZGUvKXt0YXJnZXQ9Il9ibGFuayJ9IGZvciB2aXN1YWxpemF0aW9uIG9mIHRyZWUgYW5kIGFzc29jaWF0ZWQgbWV0YWRhdGEuICAKCioqT3RoZXIgdmFsdWFibGUgcmVzb3VyY2VzKioKCi0tIFtSIE1hcmtkb3duOiBUaGUgRGVmaW5pdGl2ZSBHdWlkZV0oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLyl7dGFyZ2V0PSJfYmxhbmsifSAgCi0tIFtrbml0cl0oaHR0cDovL3lpaHVpLm5hbWUva25pdHIvKXt0YXJnZXQ9Il9ibGFuayJ9IHR1dG9yaWFscy4gRmFudGFzdGljIHNpdGUhICAKLS0gW01pY3JvYmlvdGEgYW5hbHlzaXMgaW4gUiwgVUNSIFdvcmtzaG9wIDIwMThdKGh0dHBzOi8vcnB1YnMuY29tL21hZGRpZVNDL1JfU09QX1VDUl9KYW5fMjAxOCl7dGFyZ2V0PSJfYmxhbmsifSBuaWNlbHkgZG9jdW1lbnRlZCB3b3JrZmxvdyB3aXRoIGV4YW1wbGVzLiAKCiMjQXBwZW5kaXggQzogU3VibWl0dGluZyBzZXF1ZW5jaW5nIGRhdGEgdG8gcHVibGljIGFyY2hpdmVzICAKCltiYWNrIHRvIHRvcF0oI2JhY2sgdG8gdG9wKQoKSXQgaXMgbm93IHRpbWUgdG8gc3VibWl0IHRoZSBkYXRhIHRvIHlvdXIgZmF2b3JpdGUgc2VxdWVuY2UgcmVhZCBhcmNoaXZlLiBXZSBzdWJtaXR0ZWQgb3V0IGRhdGEgdG8gdGhlIFtFdXJvcGVhbiBOdWNsZW90aWRlIEFyY2hpdmUgKEVOQSldKGh0dHBzOi8vd3d3LmViaS5hYy51ay9lbmEpLiBUaGUgRU5BIGRvZXMgbm90IGxpa2UgUkFXIGRhdGEgYW5kIHByZWZlcnMgdG8gaGF2ZSBwcmltZXJzIHJlbW92ZWQuIFNvIHdlIHN1Ym1pdHRlZCB0aGUgdHJpbW1lZCBGYXN0cSBmaWxlcyB0byB0aGUgRU5BLiBZb3UgY2FuIGZpbmQgdGhlc2UgZGF0YSB1bmRlciB0aGUgc3R1ZHkgYWNjZXNzaW9uIG51bWJlciAqKlBSSkVCMjgzOTcqKi4gVGhlIFJBVyBmaWxlcyBvbiBvdXIgZmlnc2hhcmUgc2l0ZS4gIFRPRE8gSU5TRVJUIERPSS9MSU5LCgoKVG8gc3VibWl0IHRvIHRoZSBFTkEgeW91IG5lZWQgdHdvIGRhdGEgdGFibGVzIChwbHVzIHlvdXIgc2VxdWVuY2UgZGF0YSkuIFRoZSBmaXJzdCBmaWxlIGRlc2NyaWJlcyB0aGUgc2FtcGxlcyBhbmQgdGhlIHNlY29uZCBmaWxlIGRlc2NyaWJlcyB0aGUgc2VxdWVuY2luZyBkYXRhLiBUaGUgb3JpZ2luYWwgZmlsZXMgY2FuIGJlIGZvdW5kIG9uIHRoZSBmaWdzaGFyZSBzaXRlLiAKCiMjQXBwZW5kaXggRDogU3BlY2lmaWMgUiBwYWNrYWdlICYgdmVyc2lvbnMgCgpbYmFjayB0byB0b3BdKCNiYWNrIHRvIHRvcCkKCkJlbG93IGFyZSB0aGUgc3BlY2lmaWMgcGFja2FnZXMgYW5kIHZlcnNpb25zIHVzZWQgaW4gdGhpcyB3b3JrZmxvdyB1c2luZyBib3RoIGBzZXNzaW9uSW5mbygpYCBhbmQgYGRldnRvb2xzOjpzZXNzaW9uX2luZm8oKWAuCgpgYGB7ciBzZXNzaW9uSW5mbywgaW5jbHVkZT1UUlVFfQpwcm9jLnRpbWUoKSAtIHB0bQpzZXNzaW9uSW5mbygpCmRldnRvb2xzOjpzZXNzaW9uX2luZm8oKQplbmRfdGltZSA8LSBTeXMudGltZSgpCmVuZF90aW1lIC0gc3RhcnRfdGltZQpgYGAKCltiYWNrIHRvIHRvcF0oI2JhY2sgdG8gdG9wKQoKIyMjQXV0aG9yIEFmZmlsaWF0aW9ucwo=